From 3072ce7dec9e2700bb67541ccd26f702021c4769 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Mon, 14 Oct 2013 18:54:36 +0200 Subject: [PATCH 01/10] Enabled assertion on content_negotiation during autotests The tests project is now void-safe Using force instead of put_left seems to work fine and is better for performance, and no need to check for precondition "not before" --- .../src/conneg_server_side.e | 2 +- .../src/parsers/common_accept_header_parser.e | 16 ++--- .../src/parsers/language_parse.e | 2 +- .../src/parsers/mime_parse.e | 2 +- .../test/conneg_server_side_test.e | 58 ++++++++++++------- .../content_negotiation/test/test-safe.ecf | 12 ++-- 6 files changed, 55 insertions(+), 37 deletions(-) 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 620fbed8..7440a799 100644 --- a/library/network/protocol/content_negotiation/src/conneg_server_side.e +++ b/library/network/protocol/content_negotiation/src/conneg_server_side.e @@ -34,7 +34,7 @@ feature -- Initialization set_charset_default (a_charset) set_encoding_default (a_encoding) ensure - mime_default_set: mime = a_mime + 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 diff --git a/library/network/protocol/content_negotiation/src/parsers/common_accept_header_parser.e b/library/network/protocol/content_negotiation/src/parsers/common_accept_header_parser.e index 9a8e983f..de3e8040 100644 --- a/library/network/protocol/content_negotiation/src/parsers/common_accept_header_parser.e +++ b/library/network/protocol/content_negotiation/src/parsers/common_accept_header_parser.e @@ -1,7 +1,7 @@ note description: "[ COMMON_ACCEPT_HEADER_PARSER, this class allows to parse Accept-Charset and Accept-Encoding headers - + ]" date: "$Date$" revision: "$Revision$" @@ -52,7 +52,7 @@ feature -- Parser Result.set_field (trim (l_header)) end - fitness_and_quality_parsed (a_field: READABLE_STRING_8; parsed_charsets: LIST [COMMON_RESULTS]): FITNESS_AND_QUALITY + fitness_and_quality_parsed (a_field: READABLE_STRING_8; a_parsed_charsets: LIST [COMMON_RESULTS]): FITNESS_AND_QUALITY -- Find the best match for a given charset/encoding against a list of charsets/encodings -- that have already been parsed by parse_common. Returns a -- tuple of the fitness value and the value of the 'q' quality parameter of @@ -82,11 +82,11 @@ feature -- Parser end if attached target.field as l_target_field then from - parsed_charsets.start + a_parsed_charsets.start until - parsed_charsets.after + a_parsed_charsets.after loop - range := parsed_charsets.item_for_iteration + range := a_parsed_charsets.item_for_iteration if attached range.field as l_range_common then if l_target_field.same_string (l_range_common) or l_target_field.same_string ("*") or l_range_common.same_string ("*") then if l_range_common.same_string (l_target_field) then @@ -105,7 +105,7 @@ feature -- Parser end end end - parsed_charsets.forth + a_parsed_charsets.forth end end create Result.make (best_fitness, best_fit_q) @@ -130,13 +130,13 @@ feature -- Parser do l_commons := commons.split (',') from - create res.make (10); + create res.make (10) l_commons.start until l_commons.after loop p_res := parse_common (l_commons.item_for_iteration) - res.put_left (p_res) + res.force (p_res) l_commons.forth end Result := quality_parsed (a_field, res) diff --git a/library/network/protocol/content_negotiation/src/parsers/language_parse.e b/library/network/protocol/content_negotiation/src/parsers/language_parse.e index bbb39cb9..b5ef3584 100644 --- a/library/network/protocol/content_negotiation/src/parsers/language_parse.e +++ b/library/network/protocol/content_negotiation/src/parsers/language_parse.e @@ -193,7 +193,7 @@ feature -- Parser l_ranges.after loop p_res := parse_language_range (l_ranges.item_for_iteration) - res.put_left (p_res) + res.force (p_res) l_ranges.forth end Result := quality_parsed (a_language, res) diff --git a/library/network/protocol/content_negotiation/src/parsers/mime_parse.e b/library/network/protocol/content_negotiation/src/parsers/mime_parse.e index 5e9dd923..8be798a8 100644 --- a/library/network/protocol/content_negotiation/src/parsers/mime_parse.e +++ b/library/network/protocol/content_negotiation/src/parsers/mime_parse.e @@ -189,7 +189,7 @@ feature -- Parser l_ranges.after loop p_res := parse_media_range (l_ranges.item_for_iteration) - res.put_left (p_res) + res.force (p_res) l_ranges.forth end Result := quality_parsed (a_mime_type, res) 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 e53d28c9..1ff829d2 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 @@ -33,8 +33,12 @@ feature -- Test routines mime_types_supported := l_types.split(',') media_variants := conneg.media_type_preference (mime_types_supported, "text/html") assert ("Expected Not Acceptable", not media_variants.is_acceptable) - assert ("Same Value at 1",mime_types_supported.at (1).is_equal (media_variants.supported_variants.at (1))) - assert ("Same count",mime_types_supported.count = media_variants.supported_variants.count) + if attached media_variants.supported_variants as l_supported_variants then + assert ("Same Value at 1",mime_types_supported.first.same_string (l_supported_variants.first)) + assert ("Same count",mime_types_supported.count = l_supported_variants.count) + 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) @@ -42,15 +46,15 @@ feature -- Test routines 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 defaul", conneg.mime_default.is_equal (media_variants.type)) + 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) --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") assert ("Expected Acceptable", media_variants.is_acceptable) assert ("Variants is dettached",media_variants.supported_variants = Void) - assert ("Variant Header", media_variants.variant_header.is_equal ("Accept")) - assert ("Media Type is application/json", media_variants.type.is_equal ("application/json")) + 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")) end @@ -67,8 +71,12 @@ feature -- Test routines charset_supported := l_charset.split(',') charset_variants := conneg.charset_preference (charset_supported, "unicode-1-1") assert ("Expected Not Acceptable", not charset_variants.is_acceptable) - assert ("Same Value at 1",charset_supported.at (1).is_equal (charset_variants.supported_variants.at (1))) - assert ("Same count",charset_supported.count = charset_variants.supported_variants.count) + if attached charset_variants.supported_variants as l_supported_variants then + assert ("Same Value at 1",charset_supported.first.same_string (l_supported_variants.first)) + assert ("Same count",charset_supported.count = l_supported_variants.count) + 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) @@ -77,7 +85,7 @@ feature -- Test routines 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", conneg.charset_default.is_equal (charset_variants.type)) + 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) @@ -85,8 +93,8 @@ feature -- Test routines 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", charset_variants.variant_header.is_equal ("Accept-Charset")) - assert ("Character Type is iso-8859-5", charset_variants.type.is_equal ("iso-8859-5")) + 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")) end test_compression_negotiation @@ -100,8 +108,12 @@ feature -- Test routines compression_supported := l_compression.split(',') compression_variants := conneg.encoding_preference (compression_supported, "gzip") assert ("Expected Not Acceptable", not compression_variants.is_acceptable) - assert ("Same Value at 1",compression_supported.at (1).is_equal (compression_variants.supported_variants.at (1))) - assert ("Same count",compression_supported.count = compression_variants.supported_variants.count) + 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 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) @@ -110,7 +122,7 @@ feature -- Test routines 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", conneg.encoding_default.is_equal (compression_variants.type)) + 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) @@ -121,8 +133,8 @@ feature -- Test routines 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", compression_variants.variant_header.is_equal ("Accept-Encoding")) - assert ("Encoding Type is gzip", compression_variants.type.is_equal ("gzip")) + 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")) end @@ -138,17 +150,21 @@ 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 ("Same Value at 1",languages_supported.at (1).is_equal (language_variants.supported_variants.at (1))) - assert ("Same count",languages_supported.count = language_variants.supported_variants.count) assert ("Variant header is void",language_variants.variant_header = Void) assert ("Language type is void",language_variants.type = 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) + else + 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 defaul", conneg.language_default.is_equal (language_variants.type)) + 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) @@ -156,10 +172,8 @@ feature -- Test routines 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", language_variants.variant_header.is_equal ("Accept-Language")) - assert ("Language Type is fr", language_variants.type.is_equal ("fr")) - - + 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")) end feature -- Implementation diff --git a/library/network/protocol/content_negotiation/test/test-safe.ecf b/library/network/protocol/content_negotiation/test/test-safe.ecf index 18eb204d..996f55a6 100644 --- a/library/network/protocol/content_negotiation/test/test-safe.ecf +++ b/library/network/protocol/content_negotiation/test/test-safe.ecf @@ -1,19 +1,23 @@ - + + /.git$ /EIFGENs$ /CVS$ /.svn$ - /.git$ - + + + - From d376f9983255f6dc6f5b7ade51e97eeff541b504 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 15 Oct 2013 23:19:12 +0200 Subject: [PATCH 02/10] Updated content_negotiation with better class names and feature names. Minor semantic changes in VARIANTS classes Factorized some code in HTTP_ACCEPT_LANGUAGE --- .../protocol/content_negotiation/README.md | 7 +- .../content_negotiation/conneg-safe.ecf | 8 +- .../protocol/content_negotiation/conneg.ecf | 8 +- .../src/conneg_server_side.e | 163 +++++---- .../fitness_and_quality.e | 19 +- .../src/parsers/http_accept_language_parser.e | 269 +++++++++++++++ ...arse.e => http_accept_media_type_parser.e} | 285 ++++++++-------- ...rser.e => http_any_accept_header_parser.e} | 295 ++++++++--------- ...arser_utilities.e => http_header_parser.e} | 24 +- .../src/parsers/language_parse.e | 312 ------------------ .../src/results/http_accept_language.e | 251 ++++++++++++++ ...mon_results.e => http_any_accept_header.e} | 9 +- .../src/results/language_results.e | 112 ------- .../content_negotiation/src/shared_conneg.e | 6 +- ...sults.e => http_accept_charset_variants.e} | 20 +- ...ults.e => http_accept_encoding_variants.e} | 23 +- ...ults.e => http_accept_language_variants.e} | 18 +- ...ts.e => http_accept_media_type_variants.e} | 20 +- .../src/variants/http_accept_variants.e | 92 ++++++ .../src/variants/variant_results.e | 91 ----- .../content_negotiation/test/application.e | 20 +- .../test/common_accept_header_parser_test.e | 10 +- .../test/conneg_server_side_test.e | 86 +++-- .../test/exception_trace.log | 5 + .../test/language_parser_test.e | 68 ++-- .../test/mime_parser_test.e | 16 +- 26 files changed, 1185 insertions(+), 1052 deletions(-) rename library/network/protocol/content_negotiation/src/{parsers => implementation}/fitness_and_quality.e (75%) create mode 100644 library/network/protocol/content_negotiation/src/parsers/http_accept_language_parser.e rename library/network/protocol/content_negotiation/src/parsers/{mime_parse.e => http_accept_media_type_parser.e} (83%) rename library/network/protocol/content_negotiation/src/parsers/{common_accept_header_parser.e => http_any_accept_header_parser.e} (56%) rename library/network/protocol/content_negotiation/src/parsers/{mime_type_parser_utilities.e => http_header_parser.e} (58%) delete mode 100644 library/network/protocol/content_negotiation/src/parsers/language_parse.e create mode 100644 library/network/protocol/content_negotiation/src/results/http_accept_language.e rename library/network/protocol/content_negotiation/src/results/{common_results.e => http_any_accept_header.e} (88%) delete mode 100644 library/network/protocol/content_negotiation/src/results/language_results.e rename library/network/protocol/content_negotiation/src/variants/{character_encoding_variant_results.e => http_accept_charset_variants.e} (67%) rename library/network/protocol/content_negotiation/src/variants/{compression_variant_results.e => http_accept_encoding_variants.e} (52%) rename library/network/protocol/content_negotiation/src/variants/{language_variant_results.e => http_accept_language_variants.e} (65%) rename library/network/protocol/content_negotiation/src/variants/{media_type_variant_results.e => http_accept_media_type_variants.e} (52%) create mode 100644 library/network/protocol/content_negotiation/src/variants/http_accept_variants.e delete mode 100644 library/network/protocol/content_negotiation/src/variants/variant_results.e create mode 100644 library/network/protocol/content_negotiation/test/exception_trace.log 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 From 88e6837222b0490d38e27b1c7c841502a0ff0a00 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 18 Oct 2013 16:18:22 +0200 Subject: [PATCH 03/10] Class renaming for content_negotiation Splitted SERVER_CONTENT_NEGOTIATION in 4 differents classes for each kind of negotiation Changed to use ITERABLE over LIST for supported variants arguments Factorized some code for http parameter parsing such as q=1.0;note="blabla" and so on Integrated within EWF --- .../src/conneg_server_side.e | 249 ------------------ ...ser.e => http_accept_language_utilities.e} | 59 ++--- ...r.e => http_accept_media_type_utilities.e} | 35 +-- ...r.e => http_any_accept_header_utilities.e} | 82 ++---- ...eader_parser.e => http_header_utilities.e} | 13 +- .../src/results/http_accept_language.e | 147 +++++------ .../src/results/http_any_accept.e | 118 +++++++++ .../src/results/http_any_accept_header.e | 89 ------- .../src/server_charset_negotiation.e | 92 +++++++ .../src/server_content_negotiation.e | 67 +++++ .../src/server_encoding_negotiation.e | 91 +++++++ .../src/server_language_negotiation.e | 93 +++++++ .../src/server_media_type_negotiation.e | 91 +++++++ .../content_negotiation/src/shared_conneg.e | 31 --- .../src/variants/http_accept_variants.e | 4 +- .../content_negotiation/test/application.e | 6 +- .../test/common_accept_header_parser_test.e | 20 +- .../test/conneg_server_side_test.e | 49 +++- .../test/exception_trace.log | 13 + .../test/language_parser_test.e | 13 +- .../test/mime_parser_test.e | 11 +- .../protocol/http/src/http_media_type.e | 95 +------ .../protocol/http/src/http_parameter_table.e | 150 +++++++++++ .../wsf/policy_driven/wsf_method_helper.e | 26 +- .../wsf/policy_driven/wsf_skeleton_handler.e | 10 +- 25 files changed, 938 insertions(+), 716 deletions(-) delete mode 100644 library/network/protocol/content_negotiation/src/conneg_server_side.e rename library/network/protocol/content_negotiation/src/parsers/{http_accept_language_parser.e => http_accept_language_utilities.e} (85%) rename library/network/protocol/content_negotiation/src/parsers/{http_accept_media_type_parser.e => http_accept_media_type_utilities.e} (91%) rename library/network/protocol/content_negotiation/src/parsers/{http_any_accept_header_parser.e => http_any_accept_header_utilities.e} (74%) rename library/network/protocol/content_negotiation/src/parsers/{http_header_parser.e => http_header_utilities.e} (73%) create mode 100644 library/network/protocol/content_negotiation/src/results/http_any_accept.e delete mode 100644 library/network/protocol/content_negotiation/src/results/http_any_accept_header.e create mode 100644 library/network/protocol/content_negotiation/src/server_charset_negotiation.e create mode 100644 library/network/protocol/content_negotiation/src/server_content_negotiation.e create mode 100644 library/network/protocol/content_negotiation/src/server_encoding_negotiation.e create mode 100644 library/network/protocol/content_negotiation/src/server_language_negotiation.e create mode 100644 library/network/protocol/content_negotiation/src/server_media_type_negotiation.e delete mode 100644 library/network/protocol/content_negotiation/src/shared_conneg.e create mode 100644 library/network/protocol/http/src/http_parameter_table.e diff --git a/library/network/protocol/content_negotiation/src/conneg_server_side.e b/library/network/protocol/content_negotiation/src/conneg_server_side.e deleted file mode 100644 index 37e4dbdc..00000000 --- a/library/network/protocol/content_negotiation/src/conneg_server_side.e +++ /dev/null @@ -1,249 +0,0 @@ -note - description: "Summary description for {CONNEG_SERVER_SIDE}. Utility class to support Server Side Content Negotiation " - date: "$Date$" - revision: "$Revision$" - description: "[ - Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1 - Server-driven Negotiation : If the selection of the best representation for a response is made by an algorithm located at the server, - it is called server-driven negotiation. Selection is based on the available representations of the response (the dimensions over which it can vary; e.g. language, content-coding, etc.) - and the contents of particular header fields in the request message or on other information pertaining to the request (such as the network address of the client). - Server-driven negotiation is advantageous when the algorithm for selecting from among the available representations is difficult to describe to the user agent, - or when the server desires to send its "best guess" to the client along with the first response (hoping to avoid the round-trip delay of a subsequent request if the "best guess" is good enough for the user). - In order to improve the server's guess, the user agent MAY include request header fields (Accept, Accept-Language, Accept-Encoding, etc.) which describe its preferences for such a response. - ]" - EIS: "name=server driven negotiation", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.", "protocol=uri" - -class - CONNEG_SERVER_SIDE - -inherit - REFACTORING_HELPER - -create - make - -feature {NONE} -- Initialization - - 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 - 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 - 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 - - initialize - -- Initialize Current - do - create accept_media_type_parser - create any_header_parser - create accept_language_parser - end - - 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. - - default_language: READABLE_STRING_8 - -- Natural language that is preferred as a response to the request. - - default_charset: READABLE_STRING_8 - -- Character set that is acceptable for the response. - - default_encoding: READABLE_STRING_8 - -- Content-coding that is acceptable in the response. - -feature -- Change Element - - set_default_media_type (a_mediatype: READABLE_STRING_8) - -- Set `default_media_type' with `a_mediatype' - do - default_media_type := a_mediatype - ensure - default_media_type_set: a_mediatype = default_media_type - end - - set_default_language (a_language: READABLE_STRING_8) - -- Set `default_language' with `a_language' - do - default_language := a_language - ensure - default_language_set: a_language = default_language - end - - set_default_charset (a_charset: READABLE_STRING_8) - -- Set `default_charset' with `a_charset' - do - default_charset := a_charset - ensure - default_charset_set: a_charset = default_charset - end - - set_default_encoding (a_encoding: READABLE_STRING_8) - -- Set `default_encoding' with `a_encoding' - do - default_encoding := a_encoding - ensure - 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): 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 - -- the requested media type, or empty in other case. - note - EIS: "name=media type", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri" - local - l_mime_match: READABLE_STRING_8 - do - create Result.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_variant_value (default_media_type) - else - Result.set_vary_header_value - - -- select the best match, server support, client preferences - 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) - else - -- Set the best match - Result.set_variant_value (l_mime_match) - Result.set_acceptable (True) - end - end - end - -feature -- Encoding Negotiation - - 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 - -- the requested Charset, or empty in other case. - note - EIS: "name=charset", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2", "protocol=uri" - local - l_charset_match: READABLE_STRING_8 - do - create Result.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_variant_value (default_charset) - else - Result.set_vary_header_value - - -- select the best match, server support, client preferences - 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) - else - -- Set the best match - Result.set_variant_value (l_charset_match) - Result.set_acceptable (True) - end - end - end - -feature -- Compression Negotiation - - 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_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 - EIS: "name=encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri" - local - l_compression_match: READABLE_STRING_8 - do - create Result.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_variant_value (default_encoding) - else - Result.set_vary_header_value - - -- select the best match, server support, client preferences - 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) - else - -- Set the best match - Result.set_variant_value (l_compression_match) - Result.set_acceptable (True) - end - end - end - -feature -- Language Negotiation - - 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_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 - EIS: "name=language", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4", "protocol=uri" - - local - l_language_match: READABLE_STRING_8 - do - create Result.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_variant_value (default_language) - else - Result.set_vary_header_value - - -- select the best match, server support, client preferences - 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) - else - -- Set the best match - Result.set_variant_value (l_language_match) - Result.set_acceptable (True) - end - end - end - -note - copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" - -end 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_utilities.e similarity index 85% rename from library/network/protocol/content_negotiation/src/parsers/http_accept_language_parser.e rename to library/network/protocol/content_negotiation/src/parsers/http_accept_language_utilities.e index 4f7b08d2..6d69f6f8 100644 --- a/library/network/protocol/content_negotiation/src/parsers/http_accept_language_parser.e +++ b/library/network/protocol/content_negotiation/src/parsers/http_accept_language_utilities.e @@ -1,6 +1,6 @@ note description: "[ - {HTTP_ACCEPT_LANGUAGE_PARSER} is encharge to parse language tags defined as follow: + {HTTP_ACCEPT_LANGUAGE_UTILITIES} is in charge to parse language tags defined as follow: Accept-Language = "Accept-Language" ":" 1#( language-l_range [ ";" "q" "=" qvalue ] ) @@ -15,13 +15,10 @@ note EIS: "name=Accept-Language", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4", "protocol=uri" class - HTTP_ACCEPT_LANGUAGE_PARSER + HTTP_ACCEPT_LANGUAGE_UTILITIES inherit - - HTTP_HEADER_PARSER - - REFACTORING_HELPER + HTTP_HEADER_UTILITIES feature -- Parser @@ -68,29 +65,22 @@ feature -- Parser 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 + best_match (a_supported: ITERABLE [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)) + create {ARRAYED_LIST [FITNESS_AND_QUALITY]} l_weighted_matches.make (0) + across a_supported as ic loop + l_fitness_and_quality := fitness_and_quality_from_list (ic.item, l_header_results) + l_fitness_and_quality.set_entity (entity_value (ic.item)) l_weighted_matches.force (l_fitness_and_quality) - a_supported.forth end --| Keep only top quality+fitness types @@ -177,7 +167,6 @@ feature {NONE} -- Implementation 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 @@ -201,23 +190,21 @@ feature {NONE} -- Implementation 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 + l_param_matches := 0 + if attached l_target.parameters as l_target_parameters then + across + l_target_parameters as ic + loop + l_element := ic.key + if + not l_element.same_string ("q") and then + l_range.has_parameter (l_element) and then + (attached ic.item as t_item and attached l_range.parameter (l_element) as r_item) and then + t_item.same_string (r_item) + then + l_param_matches := l_param_matches + 1 + end end - l_keys.forth end if l_range_type.same_string (l_target_type) then l_fitness := 100 @@ -239,7 +226,7 @@ feature {NONE} -- Implementation 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") + l_element := l_range.parameter ("q") if l_element /= Void then l_best_fit_q := l_element.to_real_64.min (l_target_q) else diff --git a/library/network/protocol/content_negotiation/src/parsers/http_accept_media_type_parser.e b/library/network/protocol/content_negotiation/src/parsers/http_accept_media_type_utilities.e similarity index 91% rename from library/network/protocol/content_negotiation/src/parsers/http_accept_media_type_parser.e rename to library/network/protocol/content_negotiation/src/parsers/http_accept_media_type_utilities.e index b29369be..c57e060a 100644 --- a/library/network/protocol/content_negotiation/src/parsers/http_accept_media_type_parser.e +++ b/library/network/protocol/content_negotiation/src/parsers/http_accept_media_type_utilities.e @@ -1,6 +1,6 @@ note description: "[ - {HTTP_ACCEPT_MEDIA_TYPE_PARSER}. is encharge to parse Accept request-header field defined as follow: + {HTTP_ACCEPT_MEDIA_TYPE_UTILITIES}. is encharge to parse Accept request-header field defined as follow: Accept = "Accept" ":" #( media-range [ accept-params ] ) @@ -20,13 +20,10 @@ EIS: "name=Accept", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri" class - HTTP_ACCEPT_MEDIA_TYPE_PARSER + HTTP_ACCEPT_MEDIA_TYPE_UTILITIES inherit - - HTTP_HEADER_PARSER - - REFACTORING_HELPER + HTTP_HEADER_UTILITIES feature -- Parser @@ -81,7 +78,7 @@ feature -- Parser Result := quality_from_list (a_mime_type, res) end - best_match (supported: LIST [READABLE_STRING_8]; header: READABLE_STRING_8): READABLE_STRING_8 + best_match (supported: ITERABLE [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] @@ -105,17 +102,12 @@ feature -- Parser l_res.forth end - create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (supported.count) + create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (0) - 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)) + across supported as ic loop + fitness_and_quality := fitness_and_quality_from_list (ic.item, l_header_results) + fitness_and_quality.set_entity (entity_value (ic.item)) weighted_matches.force (fitness_and_quality) - supported.forth end --| Keep only top quality+fitness types @@ -236,13 +228,9 @@ feature {NONE} -- Implementation ) then if attached target.parameters as l_keys then - from - param_matches := 0 - l_keys.start - until - l_keys.after - loop - element := l_keys.key_for_iteration + param_matches := 0 + across l_keys as ic loop + element := ic.key if not element.same_string ("q") and then range.has_parameter (element) and then @@ -251,7 +239,6 @@ feature {NONE} -- Implementation then param_matches := param_matches + 1 end - l_keys.forth end end if l_range_type.same_string (l_target_type) then diff --git a/library/network/protocol/content_negotiation/src/parsers/http_any_accept_header_parser.e b/library/network/protocol/content_negotiation/src/parsers/http_any_accept_header_utilities.e similarity index 74% rename from library/network/protocol/content_negotiation/src/parsers/http_any_accept_header_parser.e rename to library/network/protocol/content_negotiation/src/parsers/http_any_accept_header_utilities.e index b0d4931f..4bb1038f 100644 --- a/library/network/protocol/content_negotiation/src/parsers/http_any_accept_header_parser.e +++ b/library/network/protocol/content_negotiation/src/parsers/http_any_accept_header_utilities.e @@ -9,56 +9,34 @@ note EIS: "name=Encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri" class - HTTP_ANY_ACCEPT_HEADER_PARSER + HTTP_ANY_ACCEPT_HEADER_UTILITIES inherit - HTTP_HEADER_PARSER + HTTP_HEADER_UTILITIES feature -- Parser - header (a_header: READABLE_STRING_8): HTTP_ANY_ACCEPT_HEADER + header (a_header: READABLE_STRING_8): HTTP_ANY_ACCEPT -- 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'}) - local - l_parts: LIST [READABLE_STRING_8] - sub_parts: LIST [READABLE_STRING_8] - p: READABLE_STRING_8 - i: INTEGER - l_header: READABLE_STRING_8 do - create Result.make - l_parts := a_header.split (';') - if l_parts.count = 1 then - Result.put ("1.0", "q") - else - from - i := 1 - until - i > l_parts.count - loop - p := l_parts [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 + create Result.make_from_string (a_header) + if Result.parameter ("q") = Void then + Result.put_parameter ("1.0", "q") end - l_header := trim (l_parts [1]) - Result.set_field (trim (l_header)) end - quality (a_field: READABLE_STRING_8; a_commons: READABLE_STRING_8): REAL_64 + quality (a_field: READABLE_STRING_8; a_header: 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 + res: ARRAYED_LIST [HTTP_ANY_ACCEPT] + p_res: HTTP_ANY_ACCEPT do - l_commons := a_commons.split (',') + l_commons := a_header.split (',') from create res.make (10) l_commons.start @@ -72,17 +50,17 @@ feature -- Parser Result := quality_from_list (a_field, res) end - best_match (a_supported: LIST [READABLE_STRING_8]; a_header: READABLE_STRING_8): READABLE_STRING_8 + best_match (a_supported: ITERABLE [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_header_results: LIST [HTTP_ANY_ACCEPT] l_weighted_matches: LIST [FITNESS_AND_QUALITY] l_res: LIST [READABLE_STRING_8] - p_res: HTTP_ANY_ACCEPT_HEADER + p_res: HTTP_ANY_ACCEPT 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) + create {ARRAYED_LIST [HTTP_ANY_ACCEPT]} l_header_results.make (l_res.count) from l_res.start until @@ -92,16 +70,11 @@ feature -- Parser 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)) + create {ARRAYED_LIST [FITNESS_AND_QUALITY]} l_weighted_matches.make (0) + across a_supported as ic loop + l_fitness_and_quality := fitness_and_quality_from_list (ic.item, l_header_results) + l_fitness_and_quality.set_entity (entity_value (ic.item)) l_weighted_matches.force (l_fitness_and_quality) - a_supported.forth end --| Keep only top quality+fitness types @@ -150,7 +123,7 @@ feature -- Parser until l_header_results.after or l_fitness_and_quality /= Void loop - if attached l_header_results.item.field as l_field then + if attached l_header_results.item.value as l_field then from l_weighted_matches.start until @@ -184,7 +157,7 @@ feature -- Parser feature {NONE} -- Implementation - fitness_and_quality_from_list (a_field: READABLE_STRING_8; a_parsed_charsets: LIST [HTTP_ANY_ACCEPT_HEADER]): FITNESS_AND_QUALITY + fitness_and_quality_from_list (a_field: READABLE_STRING_8; a_parsed_charsets: LIST [HTTP_ANY_ACCEPT]): 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 @@ -194,15 +167,15 @@ feature {NONE} -- Implementation best_fitness: INTEGER target_q: REAL_64 best_fit_q: REAL_64 - target: HTTP_ANY_ACCEPT_HEADER - range: HTTP_ANY_ACCEPT_HEADER + target: HTTP_ANY_ACCEPT + range: HTTP_ANY_ACCEPT element: detachable READABLE_STRING_8 l_fitness: INTEGER do best_fitness := -1 best_fit_q := 0.0 target := header (a_field) - if attached target.item ("q") as q and then q.is_double then + if attached target.parameter ("q") as q and then q.is_double then target_q := q.to_double if target_q < 0.0 then target_q := 0.0 @@ -212,14 +185,14 @@ feature {NONE} -- Implementation else target_q := 1.0 end - if attached target.field as l_target_field then + if attached target.value as l_target_field then from a_parsed_charsets.start until a_parsed_charsets.after loop range := a_parsed_charsets.item_for_iteration - if attached range.field as l_range_common then + if attached range.value as l_range_common then if l_target_field.same_string (l_range_common) or l_target_field.same_string ("*") or l_range_common.same_string ("*") then if l_range_common.same_string (l_target_field) then l_fitness := 100 @@ -228,7 +201,7 @@ feature {NONE} -- Implementation end if l_fitness > best_fitness then best_fitness := l_fitness - element := range.item ("q") + element := range.parameter ("q") if element /= Void then best_fit_q := element.to_double.min (target_q) else @@ -243,7 +216,7 @@ feature {NONE} -- Implementation create Result.make (best_fitness, best_fit_q) end - quality_from_list (a_field: READABLE_STRING_8; a_parsed_common: LIST [HTTP_ANY_ACCEPT_HEADER]): REAL_64 + quality_from_list (a_field: READABLE_STRING_8; a_parsed_common: LIST [HTTP_ANY_ACCEPT]): 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 @@ -252,7 +225,6 @@ feature {NONE} -- Implementation Result := fitness_and_quality_from_list (a_field, a_parsed_common).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)" diff --git a/library/network/protocol/content_negotiation/src/parsers/http_header_parser.e b/library/network/protocol/content_negotiation/src/parsers/http_header_utilities.e similarity index 73% rename from library/network/protocol/content_negotiation/src/parsers/http_header_parser.e rename to library/network/protocol/content_negotiation/src/parsers/http_header_utilities.e index 0d08278e..9f739088 100644 --- a/library/network/protocol/content_negotiation/src/parsers/http_header_parser.e +++ b/library/network/protocol/content_negotiation/src/parsers/http_header_utilities.e @@ -1,11 +1,14 @@ note - description: "Summary description for {HTTP_HEADER_PARSER}." + description: "Summary description for {HTTP_HEADER_UTILITIES}." author: "" date: "$Date$" revision: "$Revision$" deferred class - HTTP_HEADER_PARSER + HTTP_HEADER_UTILITIES + +inherit + REFACTORING_HELPER feature {NONE} -- Helpers @@ -16,13 +19,13 @@ feature {NONE} -- Helpers do p := a_str.index_of (';', 1) if p > 0 then - Result := trim (a_str.substring (1, p - 1)) + Result := trimmed_string (a_str.substring (1, p - 1)) else - Result := trim (a_str.string) + Result := trimmed_string (a_str.string) end end - trim (a_string: READABLE_STRING_8): STRING_8 + trimmed_string (a_string: READABLE_STRING_8): STRING_8 -- trim whitespace from the beginning and end of a string -- `a_string' require 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 index afbf8a9c..9b6b9be9 100644 --- a/library/network/protocol/content_negotiation/src/results/http_accept_language.e +++ b/library/network/protocol/content_negotiation/src/results/http_accept_language.e @@ -28,74 +28,50 @@ feature {NONE} -- Initialization -- 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 + i := a_accept_language_item.index_of (';', 1) + if i > 0 then + make_with_language (trimmed_string (a_accept_language_item.substring (1, i - 1))) + create parameters.make_from_substring (a_accept_language_item, i + 1, a_accept_language_item.count) + check attached parameters as l_params and then not l_params.has_error end + else + make_with_language (trimmed_string (a_accept_language_item)) 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 attached parameter ("q") as q then if q.same_string ("1") then --| Use 1.0 formatting - put ("1.0", "q") + put_parameter ("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 ? + put_parameter ("0.0", "q") elseif r >= 1.0 then quality := 1.0 + put_parameter ("1.0", "q") else quality := r end else - put ("1.0", "q") + put_parameter ("1.0", "q") quality := 1.0 end else - put ("1.0", "q") + put_parameter ("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 + set_language_range (a_lang_tag) ensure language_range_set: language_range.same_string (a_lang_tag) and a_lang_tag /= language_range end @@ -119,7 +95,7 @@ feature {NONE} -- Initialization initialize -- Initialize Current do - create params.make (2) + create parameters.make (1) quality := 1.0 end @@ -148,37 +124,24 @@ feature -- Status report 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_range (a_lang_range: READABLE_STRING_8) + local + i: INTEGER + do + create language_range.make_from_string (a_lang_range) + i := a_lang_range.index_of ('-', 1) + if i > 0 then + language := a_lang_range.substring (1, i - 1) + specialization := a_lang_range.substring (i + 1, a_lang_range.count) + else + language := a_lang_range + end + ensure + language_range_set: language_range.same_string (a_lang_range) and a_lang_range /= language_range + end + set_language (a_root_lang: READABLE_STRING_8) -- Set `'anguage' with `a_root_lang' require @@ -199,19 +162,47 @@ feature -- Element change 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' +feature -- Parameters: Access + + parameter (a_key: READABLE_STRING_8): detachable READABLE_STRING_8 + -- Parameter associated with `a_key', if present + -- otherwise default value of type `STRING' do - if params.has_key (key) then - params.replace (new, key) - else - params.force (new, key) + if attached parameters as l_params then + Result := l_params.item (a_key) end + end + + parameters: detachable HTTP_PARAMETER_TABLE + -- Table of all parameters for the media range + +feature -- Parameters: Status report + + has_parameter (a_key: READABLE_STRING_8): BOOLEAN + -- Is there an parameter in the parameters table with key `a_key'? + do + if attached parameters as l_params then + Result := l_params.has_key (a_key) + end + end + +feature -- Parameters: Change + + put_parameter (a_value: READABLE_STRING_8; a_key: READABLE_STRING_8) + -- Insert `a_value' with `a_key' if there is no other item + -- associated with the same key. If present, replace + -- the old value with `a_value' + local + l_parameters: like parameters + do + l_parameters := parameters + if l_parameters = Void then + create l_parameters.make (1) + parameters := l_parameters + end + l_parameters.force (a_value, a_key) ensure - has_key: params.has_key (key) - has_item: params.has_item (new) + is_set: attached parameters as l_params and then (l_params.has_key (a_key) and l_params.has_item (a_value)) end feature {NONE} -- Implementation diff --git a/library/network/protocol/content_negotiation/src/results/http_any_accept.e b/library/network/protocol/content_negotiation/src/results/http_any_accept.e new file mode 100644 index 00000000..f35bbb4d --- /dev/null +++ b/library/network/protocol/content_negotiation/src/results/http_any_accept.e @@ -0,0 +1,118 @@ +note + description: "Object that represents a results after parsing Accept(-*) headers." + date: "$Date$" + revision: "$Revision$" + +class + HTTP_ANY_ACCEPT + +inherit + REFACTORING_HELPER + + DEBUG_OUTPUT + +create + make_from_string + +feature -- Initialization + + make_from_string (a_string: READABLE_STRING_8) + local + l_parts: LIST [READABLE_STRING_8] + sub_parts: LIST [READABLE_STRING_8] + p: READABLE_STRING_8 + i: INTEGER + do + initialize + i := a_string.index_of (';', 1) + if i > 0 then + set_value (trimmed_string (a_string.substring (1, i - 1))) + create parameters.make_from_substring (a_string, i + 1, a_string.count) + else + set_value (trimmed_string (a_string)) + end + end + + initialize + do + end + +feature -- Access + + value: READABLE_STRING_8 + -- Value composing an Accept(-*) header value + +feature -- Access: parameters + + parameter (a_key: READABLE_STRING_8): detachable READABLE_STRING_8 + -- Item associated with `a_key', if present + -- otherwise default value of type `STRING' + do + if attached parameters as l_parameters then + Result := l_parameters.item (a_key) + end + end + + parameters: detachable HTTP_PARAMETER_TABLE + -- Table of all parameters for the media range + +feature -- Status Report + + has_parameter (a_key: READABLE_STRING_8): BOOLEAN + -- Is there an item in the table with key `a_key'? + do + if attached parameters as l_parameters then + Result := l_parameters.has_key (a_key) + end + end + +feature -- Element change + + set_value (v: READABLE_STRING_8) + -- Set `value' with `v' + do + value := v + ensure + value_set: attached value as l_value implies l_value.same_string (v) + end + + put_parameter (a_value: READABLE_STRING_8; a_key: READABLE_STRING_8) + -- Insert `a_value' with `a_key' if there is no other item + -- associated with the same key. If present, replace + -- the old value with `a_value' + local + l_parameters: like parameters + do + l_parameters := parameters + if l_parameters = Void then + create l_parameters.make (1) + parameters := l_parameters + end + l_parameters.force (a_value, a_key) + ensure + is_set: attached parameters as l_params and then (l_params.has_key (a_key) and l_params.has_item (a_value)) + end + +feature -- Status Report + + debug_output: STRING + -- String that should be displayed in debugger to represent `Current'. + do + create Result.make_from_string (value) + 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 + +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_any_accept_header.e b/library/network/protocol/content_negotiation/src/results/http_any_accept_header.e deleted file mode 100644 index 502ee3cc..00000000 --- a/library/network/protocol/content_negotiation/src/results/http_any_accept_header.e +++ /dev/null @@ -1,89 +0,0 @@ -note - description: "Object that represents a results after parsing Accept-* headers." - date: "$Date$" - revision: "$Revision$" - -class - HTTP_ANY_ACCEPT_HEADER - -create - make - -feature -- Initialization - - make - do - create params.make (2) - end - -feature -- Access - - field: detachable STRING - - item (a_key: STRING): detachable STRING - -- Item associated with `a_key', if present - -- otherwise default value of type `STRING' - do - Result := params.item (a_key) - end - - keys: LIST [STRING] - -- arrays of currents keys - local - res: ARRAYED_LIST [STRING] - do - create res.make_from_array (params.current_keys) - Result := res - end - - params: HASH_TABLE [STRING, STRING] - -- Table of all 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_field (a_field: STRING) - -- Set type with `a_field' - do - field := a_field - ensure - field_set: attached field as l_field implies l_field = a_field - end - - put (new: STRING; key: STRING) - -- Insert `new' with `key' if there is no other item - -- associated with the same key. If present, replace - -- the old value with `new' - do - if params.has_key (key) then - params.replace (new, key) - else - params.force (new, key) - end - ensure - has_key: params.has_key (key) - has_item: params.has_item (new) - end - -feature -- Status Report - - 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/server_charset_negotiation.e b/library/network/protocol/content_negotiation/src/server_charset_negotiation.e new file mode 100644 index 00000000..9652fcf3 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/server_charset_negotiation.e @@ -0,0 +1,92 @@ +note + description: "Summary description for {SERVER_CHARSET_NEGOTIATION}. Utility class to support Server Side Content Negotiation on charset " + date: "$Date$" + revision: "$Revision$" + description: "[ + Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1 + Server-driven Negotiation : If the selection of the best representation for a response is made by an algorithm located at the server, + it is called server-driven negotiation. Selection is based on the available representations of the response (the dimensions over which it can vary; e.g. language, content-coding, etc.) + and the contents of particular header fields in the request message or on other information pertaining to the request (such as the network address of the client). + Server-driven negotiation is advantageous when the algorithm for selecting from among the available representations is difficult to describe to the user agent, + or when the server desires to send its "best guess" to the client along with the first response (hoping to avoid the round-trip delay of a subsequent request if the "best guess" is good enough for the user). + In order to improve the server's guess, the user agent MAY include request header fields (Accept, Accept-Language, Accept-Encoding, etc.) which describe its preferences for such a response. + ]" + EIS: "name=server driven negotiation", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.", "protocol=uri" + +class + SERVER_CHARSET_NEGOTIATION + +inherit + REFACTORING_HELPER + +create + make + +feature {NONE} -- Initialization + + make (a_charset_dft: READABLE_STRING_8) + do + create accept_charset_utilities + set_default_charset (a_charset_dft) + ensure + default_charset_set: default_charset = a_charset_dft + end + + accept_charset_utilities: HTTP_ANY_ACCEPT_HEADER_UTILITIES + -- Charset + +feature -- Access: Server Side Defaults Formats + + default_charset: READABLE_STRING_8 + -- Character set that is acceptable for the response. + +feature -- Change Element + + set_default_charset (a_charset: READABLE_STRING_8) + -- Set `default_charset' with `a_charset' + do + default_charset := a_charset + ensure + default_charset_set: a_charset = default_charset + end + +feature -- Charset Negotiation + + preference (a_server_charset_supported: ITERABLE [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 + -- the requested Charset, or empty in other case. + note + EIS: "name=charset", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2", "protocol=uri" + local + l_charset_match: READABLE_STRING_8 + do + create Result.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_variant_value (default_charset) + else + Result.set_vary_header_value + + -- select the best match, server support, client preferences + l_charset_match := accept_charset_utilities.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) + else + -- Set the best match + Result.set_variant_value (l_charset_match) + Result.set_acceptable (True) + end + end + end + + +note + copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + +end diff --git a/library/network/protocol/content_negotiation/src/server_content_negotiation.e b/library/network/protocol/content_negotiation/src/server_content_negotiation.e new file mode 100644 index 00000000..a8f9319b --- /dev/null +++ b/library/network/protocol/content_negotiation/src/server_content_negotiation.e @@ -0,0 +1,67 @@ +note + description: "Summary description for {SERVER_CONTENT_NEGOTIATION}. Utility class to support Server Side Content Negotiation " + date: "$Date$" + revision: "$Revision$" + description: "[ + Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1 + Server-driven Negotiation : If the selection of the best representation for a response is made by an algorithm located at the server, + it is called server-driven negotiation. Selection is based on the available representations of the response (the dimensions over which it can vary; e.g. language, content-coding, etc.) + and the contents of particular header fields in the request message or on other information pertaining to the request (such as the network address of the client). + Server-driven negotiation is advantageous when the algorithm for selecting from among the available representations is difficult to describe to the user agent, + or when the server desires to send its "best guess" to the client along with the first response (hoping to avoid the round-trip delay of a subsequent request if the "best guess" is good enough for the user). + In order to improve the server's guess, the user agent MAY include request header fields (Accept, Accept-Language, Accept-Encoding, etc.) which describe its preferences for such a response. + ]" + EIS: "name=server driven negotiation", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.", "protocol=uri" + +class + SERVER_CONTENT_NEGOTIATION + +inherit + SERVER_MEDIA_TYPE_NEGOTIATION + rename + make as make_media_type, + preference as media_type_preference + end + + SERVER_LANGUAGE_NEGOTIATION + rename + make as make_language, + preference as language_preference + end + + SERVER_CHARSET_NEGOTIATION + rename + make as make_charset, + preference as charset_preference + end + + SERVER_ENCODING_NEGOTIATION + rename + make as make_encoding, + preference as encoding_preference + end + +create + make + +feature {NONE} -- Initialization + + 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 + make_media_type (a_mediatype_dft) + make_language (a_language_dft) + make_charset (a_charset_dft) + make_encoding (a_encoding_dft) + ensure + 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 + +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/server_encoding_negotiation.e b/library/network/protocol/content_negotiation/src/server_encoding_negotiation.e new file mode 100644 index 00000000..65e04bb2 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/server_encoding_negotiation.e @@ -0,0 +1,91 @@ +note + description: "Summary description for {SERVER_ENCODING_NEGOTIATION}. Utility class to support Server Side Content Negotiation on encoding" + date: "$Date$" + revision: "$Revision$" + description: "[ + Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1 + Server-driven Negotiation : If the selection of the best representation for a response is made by an algorithm located at the server, + it is called server-driven negotiation. Selection is based on the available representations of the response (the dimensions over which it can vary; e.g. language, content-coding, etc.) + and the contents of particular header fields in the request message or on other information pertaining to the request (such as the network address of the client). + Server-driven negotiation is advantageous when the algorithm for selecting from among the available representations is difficult to describe to the user agent, + or when the server desires to send its "best guess" to the client along with the first response (hoping to avoid the round-trip delay of a subsequent request if the "best guess" is good enough for the user). + In order to improve the server's guess, the user agent MAY include request header fields (Accept, Accept-Language, Accept-Encoding, etc.) which describe its preferences for such a response. + ]" + EIS: "name=server driven negotiation", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.", "protocol=uri" + +class + SERVER_ENCODING_NEGOTIATION + +inherit + REFACTORING_HELPER + +create + make + +feature {NONE} -- Initialization + + make (a_encoding_dft: READABLE_STRING_8) + do + create accept_encoding_utilities + set_default_encoding (a_encoding_dft) + ensure + default_encoding_set: default_encoding = a_encoding_dft + end + + accept_encoding_utilities: HTTP_ANY_ACCEPT_HEADER_UTILITIES + -- Encoding + +feature -- Access: Server Side Defaults Formats + + default_encoding: READABLE_STRING_8 + -- Content-coding that is acceptable in the response. + +feature -- Change Element + + set_default_encoding (a_encoding: READABLE_STRING_8) + -- Set `default_encoding' with `a_encoding' + do + default_encoding := a_encoding + ensure + default_encoding_set: a_encoding = default_encoding + end + +feature -- Encoding Negotiation + + preference (a_server_encoding_supported: ITERABLE [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_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 + EIS: "name=encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri" + local + l_compression_match: READABLE_STRING_8 + do + create Result.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_variant_value (default_encoding) + else + Result.set_vary_header_value + + -- select the best match, server support, client preferences + l_compression_match := accept_encoding_utilities.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) + else + -- Set the best match + Result.set_variant_value (l_compression_match) + Result.set_acceptable (True) + end + end + end + +note + copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + +end diff --git a/library/network/protocol/content_negotiation/src/server_language_negotiation.e b/library/network/protocol/content_negotiation/src/server_language_negotiation.e new file mode 100644 index 00000000..f01d51d2 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/server_language_negotiation.e @@ -0,0 +1,93 @@ +note + description: "Summary description for {SERVER_LANGUAGE_NEGOTIATION}. Utility class to support Server Side Content Negotiation on language" + date: "$Date$" + revision: "$Revision$" + description: "[ + Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1 + Server-driven Negotiation : If the selection of the best representation for a response is made by an algorithm located at the server, + it is called server-driven negotiation. Selection is based on the available representations of the response (the dimensions over which it can vary; e.g. language, content-coding, etc.) + and the contents of particular header fields in the request message or on other information pertaining to the request (such as the network address of the client). + Server-driven negotiation is advantageous when the algorithm for selecting from among the available representations is difficult to describe to the user agent, + or when the server desires to send its "best guess" to the client along with the first response (hoping to avoid the round-trip delay of a subsequent request if the "best guess" is good enough for the user). + In order to improve the server's guess, the user agent MAY include request header fields (Accept, Accept-Language, Accept-Encoding, etc.) which describe its preferences for such a response. + ]" + EIS: "name=server driven negotiation", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.", "protocol=uri" + +class + SERVER_LANGUAGE_NEGOTIATION + +inherit + REFACTORING_HELPER + +create + make + +feature {NONE} -- Initialization + + make (a_language_dft: READABLE_STRING_8) + do + create accept_language_utilities + set_default_language (a_language_dft) + ensure + default_language_set: default_language = a_language_dft + end + + accept_language_utilities: HTTP_ACCEPT_LANGUAGE_UTILITIES + -- Language + +feature -- Access: Server Side Defaults Formats + + default_language: READABLE_STRING_8 + -- Natural language that is preferred as a response to the request. + +feature -- Change Element + + set_default_language (a_language: READABLE_STRING_8) + -- Set `default_language' with `a_language' + do + default_language := a_language + ensure + default_language_set: a_language = default_language + end + +feature -- Language Negotiation + + preference (a_server_language_supported: ITERABLE [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_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 + EIS: "name=language", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4", "protocol=uri" + + local + l_language_match: READABLE_STRING_8 + do + create Result.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_variant_value (default_language) + else + Result.set_vary_header_value + + -- select the best match, server support, client preferences + l_language_match := accept_language_utilities.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) + else + -- Set the best match + Result.set_variant_value (l_language_match) + Result.set_acceptable (True) + end + end + end + +note + copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + +end diff --git a/library/network/protocol/content_negotiation/src/server_media_type_negotiation.e b/library/network/protocol/content_negotiation/src/server_media_type_negotiation.e new file mode 100644 index 00000000..76c84a70 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/server_media_type_negotiation.e @@ -0,0 +1,91 @@ +note + description: "Summary description for {SERVER_MEDIA_TYPE_NEGOTIATION}. Utility class to support Server Side Content Negotiation on media type" + date: "$Date$" + revision: "$Revision$" + description: "[ + Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1 + Server-driven Negotiation : If the selection of the best representation for a response is made by an algorithm located at the server, + it is called server-driven negotiation. Selection is based on the available representations of the response (the dimensions over which it can vary; e.g. language, content-coding, etc.) + and the contents of particular header fields in the request message or on other information pertaining to the request (such as the network address of the client). + Server-driven negotiation is advantageous when the algorithm for selecting from among the available representations is difficult to describe to the user agent, + or when the server desires to send its "best guess" to the client along with the first response (hoping to avoid the round-trip delay of a subsequent request if the "best guess" is good enough for the user). + In order to improve the server's guess, the user agent MAY include request header fields (Accept, Accept-Language, Accept-Encoding, etc.) which describe its preferences for such a response. + ]" + EIS: "name=server driven negotiation", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.", "protocol=uri" + +class + SERVER_MEDIA_TYPE_NEGOTIATION + +inherit + REFACTORING_HELPER + +create + make + +feature {NONE} -- Initialization + + make (a_mediatype_dft: READABLE_STRING_8) + do + create accept_media_type_utilities + set_default_media_type (a_mediatype_dft) + ensure + default_media_type_set: default_media_type = a_mediatype_dft + end + + accept_media_type_utilities: HTTP_ACCEPT_MEDIA_TYPE_UTILITIES + -- MIME + +feature -- Access: Server Side Defaults Formats + + default_media_type: READABLE_STRING_8 + -- Media type which is acceptable for the response. + +feature -- Change Element + + set_default_media_type (a_mediatype: READABLE_STRING_8) + -- Set `default_media_type' with `a_mediatype' + do + default_media_type := a_mediatype + ensure + default_media_type_set: a_mediatype = default_media_type + end + +feature -- Media Type Negotiation + + preference (a_mime_types_supported: ITERABLE [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 + -- the requested media type, or empty in other case. + note + EIS: "name=media type", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri" + local + l_mime_match: READABLE_STRING_8 + do + create Result.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_variant_value (default_media_type) + else + Result.set_vary_header_value + + -- select the best match, server support, client preferences + l_mime_match := accept_media_type_utilities.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) + else + -- Set the best match + Result.set_variant_value (l_mime_match) + Result.set_acceptable (True) + end + end + end + +note + copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + +end diff --git a/library/network/protocol/content_negotiation/src/shared_conneg.e b/library/network/protocol/content_negotiation/src/shared_conneg.e deleted file mode 100644 index fdfd9fdd..00000000 --- a/library/network/protocol/content_negotiation/src/shared_conneg.e +++ /dev/null @@ -1,31 +0,0 @@ -note - description: "Summary description for {SHARED_CONNEG}." - date: "$Date$" - revision: "$Revision$" - -class - SHARED_CONNEG - -feature - - Mime: HTTP_ACCEPT_MEDIA_TYPE_PARSER - once - create Result - end - - Common: HTTP_ANY_ACCEPT_HEADER_PARSER - -- Charset and Encoding - once - create Result - end - - Language: HTTP_ACCEPT_LANGUAGE_PARSER - once - create Result - end - -note - copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" - -end 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 index 86295d8b..78a39be2 100644 --- a/library/network/protocol/content_negotiation/src/variants/http_accept_variants.e +++ b/library/network/protocol/content_negotiation/src/variants/http_accept_variants.e @@ -28,7 +28,7 @@ feature -- Access -- 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] + supported_variants: detachable ITERABLE [READABLE_STRING_8] -- Set of supported variants for the response variant_value: detachable READABLE_STRING_8 @@ -77,7 +77,7 @@ feature -- Change Element is_acceptable_set: is_acceptable = b end - set_supported_variants (a_supported: LIST [READABLE_STRING_8]) + set_supported_variants (a_supported: ITERABLE [READABLE_STRING_8]) -- Set `supported variants' with `a_supported' do supported_variants := a_supported diff --git a/library/network/protocol/content_negotiation/test/application.e b/library/network/protocol/content_negotiation/test/application.e index b7152129..74cd00d7 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 : HTTP_ACCEPT_MEDIA_TYPE_PARSER + mime_parse : HTTP_ACCEPT_MEDIA_TYPE_UTILITIES accept : STRING - charset_parse : HTTP_ANY_ACCEPT_HEADER_PARSER - language : HTTP_ACCEPT_LANGUAGE_PARSER + charset_parse : HTTP_ANY_ACCEPT_HEADER_UTILITIES + language : HTTP_ACCEPT_LANGUAGE_UTILITIES do create mime_parse -- parse_result := mime_parse.parse_mime_type ("application/xhtml;q=0.5") 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 3c3dcbf6..1c736576 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,26 +23,24 @@ feature {NONE} -- Events feature -- Helpers - format (a_common: HTTP_ANY_ACCEPT_HEADER): STRING + format (a_common: HTTP_ANY_ACCEPT): STRING -- Representation of the current object do create Result.make_from_string ("(") - if attached a_common.field as t then + if attached a_common.value as t then Result.append_string ("'" + t + "',") end Result.append_string (" {") - from - a_common.params.start - until - a_common.params.after - loop - Result.append ("'" + a_common.params.key_for_iteration + "':'" + a_common.params.item_for_iteration + "',"); - a_common.params.forth + if attached a_common.parameters as l_parameters then + across + l_parameters as ic + loop + Result.append ("'" + ic.key + "':'" + ic.item + "',"); + end end Result.append ("})") end - feature -- Test routines test_parse_charsets @@ -74,6 +72,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 : HTTP_ANY_ACCEPT_HEADER_PARSER + parser : HTTP_ANY_ACCEPT_HEADER_UTILITIES 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 d0c1e518..5ba5930d 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 @@ -34,8 +34,8 @@ feature -- Test routines media_variants := conneg.media_type_preference (mime_types_supported, "text/html") assert ("Expected Not Acceptable", not media_variants.is_acceptable) if attached media_variants.supported_variants as l_supported_variants then - assert ("Same Value at 1",mime_types_supported.first.same_string (l_supported_variants.first)) - assert ("Same count",mime_types_supported.count = l_supported_variants.count) + assert ("Same Value at 1", same_text (first_of (mime_types_supported), first_of (l_supported_variants))) + assert ("Same count", count_of (mime_types_supported) = count_of (l_supported_variants)) else assert ("Has supported_variants results", False) end @@ -72,8 +72,8 @@ feature -- Test routines 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 - assert ("Same Value at 1",charset_supported.first.same_string (l_supported_variants.first)) - assert ("Same count",charset_supported.count = l_supported_variants.count) + assert ("Same Value at 1", same_text (first_of (charset_supported), first_of (l_supported_variants))) + assert ("Same count",charset_supported.count = count_of (l_supported_variants)) else assert("Has supported_variants results", False) end @@ -109,8 +109,8 @@ 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.first.same_string (l_supported_variants.first)) - assert ("Same count",compression_supported.count = l_supported_variants.count) + assert ("Same Value at 1", same_text (first_of (compression_supported), first_of (l_supported_variants))) + assert ("Same count",compression_supported.count = count_of (l_supported_variants)) else assert ("Has supported_variants results", False) end @@ -153,8 +153,8 @@ feature -- Test routines 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) + assert ("Same Value at 1", same_text (first_of (languages_supported), first_of (l_supported_variants))) + assert ("Same count",languages_supported.count = count_of (l_supported_variants)) else assert ("Has supported variants results", False) end @@ -175,5 +175,36 @@ feature -- Test routines end feature -- Implementation - conneg : CONNEG_SERVER_SIDE + conneg : SERVER_CONTENT_NEGOTIATION + + same_text (s1,s2: detachable READABLE_STRING_8): BOOLEAN + do + if s1 = Void then + Result := s2 = Void + elseif s2 = Void then + Result := False + else + Result := s1.same_string (s2) + end + end + + count_of (i: ITERABLE [READABLE_STRING_8]): INTEGER + do + across + i as ic + loop + Result := Result + 1 + end + end + + first_of (i: ITERABLE [READABLE_STRING_8]): detachable READABLE_STRING_8 + do + across + i as ic + until + ic.item /= Void + loop + end + end + end diff --git a/library/network/protocol/content_negotiation/test/exception_trace.log b/library/network/protocol/content_negotiation/test/exception_trace.log index 3d5d5442..e268a695 100644 --- a/library/network/protocol/content_negotiation/test/exception_trace.log +++ b/library/network/protocol/content_negotiation/test/exception_trace.log @@ -3,3 +3,16 @@ Catcall detected in {READABLE_STRING_8}.make_from_string for arg#1: expected REA 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 +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 +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 +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 42acbbb8..fe937a20 100644 --- a/library/network/protocol/content_negotiation/test/language_parser_test.e +++ b/library/network/protocol/content_negotiation/test/language_parser_test.e @@ -34,14 +34,11 @@ feature -- Helpers Result.append_string (" '" + st + "',") end Result.append_string (" {") - if attached a_language.params as l_params then - from - l_params.start - until - l_params.after + if attached a_language.parameters as l_params then + across + l_params as ic loop - Result.append ("'" + l_params.key_for_iteration + "':'"+ l_params.item_for_iteration + "',"); - l_params.forth + Result.append ("'" + ic.key + "':'"+ ic.item + "',"); end end Result.append ("})") @@ -137,7 +134,7 @@ feature -- Test routines - parser : HTTP_ACCEPT_LANGUAGE_PARSER + parser : HTTP_ACCEPT_LANGUAGE_UTILITIES 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 0dbe8034..2a861868 100644 --- a/library/network/protocol/content_negotiation/test/mime_parser_test.e +++ b/library/network/protocol/content_negotiation/test/mime_parser_test.e @@ -38,13 +38,10 @@ feature -- Helper end Result.append_string (" {") if attached a_mediatype.parameters as l_params then - from - l_params.start - until - l_params.after + across + l_params as ic loop - Result.append ("'" + l_params.key_for_iteration + "':'" + l_params.item_for_iteration + "',"); - l_params.forth + Result.append ("'" + ic.key + "':'" + ic.item + "',"); end end Result.append ("})") @@ -148,7 +145,7 @@ feature -- Test routines - parser : HTTP_ACCEPT_MEDIA_TYPE_PARSER + parser : HTTP_ACCEPT_MEDIA_TYPE_UTILITIES end diff --git a/library/network/protocol/http/src/http_media_type.e b/library/network/protocol/http/src/http_media_type.e index fbe67cb2..d2d9614e 100644 --- a/library/network/protocol/http/src/http_media_type.e +++ b/library/network/protocol/http/src/http_media_type.e @@ -63,7 +63,6 @@ feature {NONE} -- Initialization t: STRING_8 i,n: INTEGER p: INTEGER - cl: CELL [INTEGER] do -- Ignore starting space (should not be any) from @@ -79,15 +78,7 @@ feature {NONE} -- Initialization p := s.index_of (';', i) if p > 0 then t := s.substring (i, p - 1) - from - create cl.put (p) - i := p + 1 - until - i >= n - loop - add_parameter_from_string (s, i, cl) - i := cl.item - end + create parameters.make_from_substring (s, p + 1, s.count) else t := s.substring (i, n) end @@ -116,7 +107,7 @@ feature {NONE} -- Initialization subtype := type end ensure - not has_error implies (create {HTTP_CONTENT_TYPE}.make_from_string (string)).same_string (string) + not has_error implies (create {HTTP_MEDIA_TYPE}.make_from_string (string)).same_string (string) end feature -- Status report @@ -149,7 +140,7 @@ feature -- Access end end - parameters: detachable HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] + parameters: detachable HTTP_PARAMETER_TABLE -- Parameters feature -- Conversion @@ -262,7 +253,7 @@ feature -- Element change -- Remove parameter named `a_name' do if attached parameters as plst then - plst.prune (a_name) + plst.remove (a_name) if plst.is_empty then parameters := Void end @@ -270,84 +261,6 @@ feature -- Element change internal_string := Void end -feature {NONE} -- Implementation - - add_parameter_from_string (s: READABLE_STRING_8; start_index: INTEGER; out_end_index: CELL [INTEGER]) - -- Add parameter from string " attribute=value " - -- and put in `out_end_index' the index after found parameter. - local - n: INTEGER - pn,pv: STRING_8 - i: INTEGER - p, q: INTEGER - err: BOOLEAN - do - n := s.count - -- Skip spaces - from - i := start_index - until - i > n or not s[i].is_space - loop - i := i + 1 - end - if s[i] = ';' then - -- empty parameter - out_end_index.replace (i + 1) - elseif i < n then - p := s.index_of ('=', i) - if p > 0 then - pn := s.substring (i, p - 1) - if p >= n then - pv := "" - out_end_index.replace (n + 1) - else - if s[p+1] = '%"' then - q := s.index_of ('%"', p + 2) - if q > 0 then - pv := s.substring (p + 2, q - 1) - from - i := q + 1 - until - i > n or not s[i].is_space - loop - i := i + 1 - end - if s[i] = ';' then - i := i + 1 - end - out_end_index.replace (i) - else - err := True - pv := "" - -- missing closing double quote. - end - else - q := s.index_of (';', p + 1) - if q = 0 then - q := n + 1 - end - pv := s.substring (p + 1, q - 1) - out_end_index.replace (q + 1) - end - pv.right_adjust - if not err then - add_parameter (pn, pv) - end - end - else - -- expecting: attribute "=" value - err := True - end - end - if err then - out_end_index.replace (n + 1) - end - has_error := has_error or err - ensure - entry_processed: out_end_index.item > start_index - end - feature {NONE} -- Internal internal_string: detachable STRING_8 diff --git a/library/network/protocol/http/src/http_parameter_table.e b/library/network/protocol/http/src/http_parameter_table.e new file mode 100644 index 00000000..21d8995c --- /dev/null +++ b/library/network/protocol/http/src/http_parameter_table.e @@ -0,0 +1,150 @@ +note + description: "[ + Table representing parameters of the form q=1.0;note="blabla";foo=bar + ]" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_PARAMETER_TABLE + +inherit + HASH_TABLE [READABLE_STRING_8, STRING_8] + redefine + empty_duplicate + end + +create + make, + make_from_string, + make_from_substring + +feature {NONE} -- Initialization + + make_from_string (s: READABLE_STRING_8) + -- Build table of parameters for `s' + do + make_from_substring (s, 1, s.count) + end + + make_from_substring (s: READABLE_STRING_8; a_start_index: INTEGER; a_end_index: INTEGER) + -- Build table of parameters for `s.substring (a_start_index, a_end_index)' + local + cl: CELL [INTEGER] + i: INTEGER + do + make (1) + from + i := a_start_index + create cl.put (i) + until + i >= a_end_index + loop + force_substring (s, i, cl) + i := cl.item + end + end + +feature -- Status report + + has_error: BOOLEAN + -- Current has error? + --| Mainly in relation with `make_from_string' and `force_substring' + +feature {NONE} -- Implementation + + force_substring (s: READABLE_STRING_8; start_index: INTEGER; out_end_index: CELL [INTEGER]) + -- Add parameter from string " attribute=value " + -- and put in `out_end_index' the index after found parameter. + local + n: INTEGER + pn,pv: STRING_8 + i: INTEGER + p, q: INTEGER + err: BOOLEAN + do + n := s.count + -- Skip spaces + from + i := start_index + until + i > n or not s[i].is_space + loop + i := i + 1 + end + if s[i] = ';' then + -- empty parameter + out_end_index.replace (i + 1) + elseif i < n then + p := s.index_of ('=', i) + if p > 0 then + pn := s.substring (i, p - 1) + if p >= n then + pv := "" + out_end_index.replace (n + 1) + else + if s[p+1] = '%"' then + q := s.index_of ('%"', p + 2) + if q > 0 then + pv := s.substring (p + 2, q - 1) + from + i := q + 1 + until + i > n or not s[i].is_space + loop + i := i + 1 + end + if s[i] = ';' then + i := i + 1 + end + out_end_index.replace (i) + else + err := True + pv := "" + -- missing closing double quote. + end + else + q := s.index_of (';', p + 1) + if q = 0 then + q := n + 1 + end + pv := s.substring (p + 1, q - 1) + out_end_index.replace (q + 1) + end + pv.right_adjust + if not err then + force (pv, pn) + end + end + else + -- expecting: attribute "=" value + err := True + end + end + if err then + out_end_index.replace (n + 1) + end + has_error := has_error or err + ensure + entry_processed: out_end_index.item > start_index + end + +feature {NONE} -- Duplication + + empty_duplicate (n: INTEGER): like Current + -- Create an empty copy of Current that can accommodate `n' items + do + Result := Precursor (n) + end + +note + copyright: "2011-2013, Jocelyn Fiat, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/server/wsf/policy_driven/wsf_method_helper.e b/library/server/wsf/policy_driven/wsf_method_helper.e index 5a28bed9..a2f280f9 100644 --- a/library/server/wsf/policy_driven/wsf_method_helper.e +++ b/library/server/wsf/policy_driven/wsf_method_helper.e @@ -167,12 +167,12 @@ feature -- Content negotiation res_attached: res /= Void a_handler_attached: a_handler /= Void local - l_conneg: CONNEG_SERVER_SIDE + l_conneg: SERVER_CONTENT_NEGOTIATION h: HTTP_HEADER - l_media: MEDIA_TYPE_VARIANT_RESULTS - l_lang: LANGUAGE_VARIANT_RESULTS - l_charset: CHARACTER_ENCODING_VARIANT_RESULTS - l_encoding: COMPRESSION_VARIANT_RESULTS + l_media: like {SERVER_CONTENT_NEGOTIATION}.media_type_preference + l_lang: like {SERVER_CONTENT_NEGOTIATION}.language_preference + l_charset: like {SERVER_CONTENT_NEGOTIATION}.charset_preference + l_encoding: like {SERVER_CONTENT_NEGOTIATION}.encoding_preference l_mime_types, l_langs, l_charsets, l_encodings: LIST [STRING] l_vary_star: BOOLEAN do @@ -188,7 +188,7 @@ feature -- Content negotiation l_conneg := a_handler.conneg (req) l_mime_types := a_handler.mime_types_supported (req) l_media := l_conneg.media_type_preference (l_mime_types, req.http_accept) - if not l_vary_star and l_mime_types.count > 1 and attached l_media.variant_header as l_media_variant then + if not l_vary_star and l_mime_types.count > 1 and attached l_media.media_type as l_media_variant then h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, l_media_variant) end if not l_media.is_acceptable then @@ -196,26 +196,26 @@ feature -- Content negotiation else l_langs := a_handler.languages_supported (req) l_lang := l_conneg.language_preference (l_langs, req.http_accept_language) - if not l_vary_star and l_langs.count > 1 and attached l_lang.variant_header as l_lang_variant then + if not l_vary_star and l_langs.count > 1 and attached l_lang.language as l_lang_variant then h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, l_lang_variant) end if not l_lang.is_acceptable then handle_not_acceptable ("None of the requested languages were acceptable", l_langs, req, res) else - if attached l_lang.type as l_language_type then + if attached l_lang.language as l_language_type then h.put_content_language (l_language_type) req.set_execution_variable (a_handler.Negotiated_language_execution_variable, l_language_type) end l_charsets := a_handler.charsets_supported (req) l_charset := l_conneg.charset_preference (l_charsets, req.http_accept_charset) - if not l_vary_star and l_charsets.count > 1 and attached l_charset.variant_header as l_charset_variant then + if not l_vary_star and l_charsets.count > 1 and attached l_charset.charset as l_charset_variant then h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, l_charset_variant) end if not l_charset.is_acceptable then handle_not_acceptable ("None of the requested character encodings were acceptable", l_charsets, req, res) else - if attached l_media.type as l_media_type then - if attached l_charset.type as l_character_type then + if attached l_media.media_type as l_media_type then + if attached l_charset.charset as l_character_type then h.put_content_type (l_media_type + "; charset=" + l_character_type) req.set_execution_variable (a_handler.Negotiated_charset_execution_variable, l_charset) else @@ -225,13 +225,13 @@ feature -- Content negotiation end l_encodings := a_handler.encodings_supported (req) l_encoding := l_conneg.encoding_preference (l_encodings, req.http_accept_encoding) - if not l_vary_star and l_encodings.count > 1 and attached l_encoding.variant_header as l_encoding_variant then + if not l_vary_star and l_encodings.count > 1 and attached l_encoding.encoding as l_encoding_variant then h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, l_encoding_variant) end if not l_encoding.is_acceptable then handle_not_acceptable ("None of the requested transfer encodings were acceptable", l_encodings, req, res) else - if attached l_encoding.type as l_compression_type then + if attached l_encoding.encoding as l_compression_type then h.put_content_encoding (l_compression_type) req.set_execution_variable (a_handler.Negotiated_encoding_execution_variable, l_compression_type) end diff --git a/library/server/wsf/policy_driven/wsf_skeleton_handler.e b/library/server/wsf/policy_driven/wsf_skeleton_handler.e index e2c92fb2..447e78bc 100644 --- a/library/server/wsf/policy_driven/wsf_skeleton_handler.e +++ b/library/server/wsf/policy_driven/wsf_skeleton_handler.e @@ -89,7 +89,7 @@ feature -- Access deferred end - conneg (req: WSF_REQUEST): CONNEG_SERVER_SIDE + conneg (req: WSF_REQUEST): SERVER_CONTENT_NEGOTIATION -- Content negotiation for `req'; -- This would normally be a once object, ignoring `req'. require @@ -103,7 +103,7 @@ feature -- Access req_attached: req /= Void deferred ensure - mime_types_supported_includes_default: Result.has (conneg (req).mime_default) + mime_types_supported_includes_default: Result.has (conneg (req).default_media_type) end languages_supported (req: WSF_REQUEST): LIST [STRING] @@ -112,7 +112,7 @@ feature -- Access req_attached: req /= Void deferred ensure - languages_supported_includes_default: Result.has (conneg (req).language_default) + languages_supported_includes_default: Result.has (conneg (req).default_language) end charsets_supported (req: WSF_REQUEST): LIST [STRING] @@ -121,7 +121,7 @@ feature -- Access req_attached: req /= Void deferred ensure - charsets_supported_includes_default: Result.has (conneg (req).charset_default) + charsets_supported_includes_default: Result.has (conneg (req).default_charset) end encodings_supported (req: WSF_REQUEST): LIST [STRING] @@ -130,7 +130,7 @@ feature -- Access req_attached: req /= Void deferred ensure - encodings_supported_includes_default: Result.has (conneg (req).encoding_default) + encodings_supported_includes_default: Result.has (conneg (req).default_encoding) end additional_variant_headers (req: WSF_REQUEST): detachable LIST [STRING] From 58dc2ec79203d78530fbe9e219e3fa9223ba931c Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 18 Oct 2013 20:44:36 +0200 Subject: [PATCH 04/10] Updated README.md for conneg --- .../protocol/content_negotiation/README.md | 117 ++---------------- 1 file changed, 12 insertions(+), 105 deletions(-) diff --git a/library/network/protocol/content_negotiation/README.md b/library/network/protocol/content_negotiation/README.md index 09a53547..bb4f9b2e 100644 --- a/library/network/protocol/content_negotiation/README.md +++ b/library/network/protocol/content_negotiation/README.md @@ -1,124 +1,31 @@ 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 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 +Using this library you can retrieve the best variant for media type, language preference, charset, and enconding/compression. Take into account that the library is under development so is expected that the API change. 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 types of negotiation (media type, language, +The class SERVER_CONTENT_NEGOTIATION 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. - How to implement Media type negotiation? - Hint: Use CONNEG_SERVER_SIDE.media_type_preference + Hint: Use SERVER_CONTENT_NEGOTIATION.media_type_preference + or SERVER_MEDIA_TYPE_NEGOTIATION.preference - How to implement Language Negotiation? - Hint: Use CONNEG_SERVER_SIDE.language_preference + Hint: Use SERVER_CONTENT_NEGOTIATION.language_preference + or SERVER_LANGUAGE_NEGOTIATION.preference -- How to implement Character encoding Negotiation? - Hint: Use CONNEG_SERVER_SIDE.charset_preference +- How to implement Character Negotiation? + Hint: Use SERVER_CONTENT_NEGOTIATION.charset_preference + or SERVER_CHARSET_NEGOTIATION.preference -- How to implement Compression Negotiation? - Hint: Use CONNEG_SERVER_SIDE.encoding_preference +- How to implement Encoding Negotiation? + Hint: Use SERVER_CONTENT_NEGOTIATION.encoding_preference + or SERVER_ENCODING_NEGOTIATION.preference There is also a [test case](test/conneg_server_side_test.e "conneg_server_side_test") where you can check how to use this class. - note - description: "Summary description for CONNEG_SERVER_SIDE. Utility class to support Server Side Content Negotiation " - author: "" - date: "$Date$" - revision: "$Revision$" - description: "[ - Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1 - Server-driven Negotiation : If the selection of the best representation for a response is made by an algorithm located at the server, - it is called server-driven negotiation. Selection is based on the available representations of the response (the dimensions over which it can vary; e.g. language, content-coding, etc.) - and the contents of particular header fields in the request message or on other information pertaining to the request (such as the network address of the client). - Server-driven negotiation is advantageous when the algorithm for selecting from among the available representations is difficult to describe to the user agent, - or when the server desires to send its "best guess" to the client along with the first response (hoping to avoid the round-trip delay of a subsequent request if the "best guess" is good enough for the user). - In order to improve the server's guess, the user agent MAY include request header fields (Accept, Accept-Language, Accept-Encoding, etc.) which describe its preferences for such a response. - ]" - - class interface - CONNEG_SERVER_SIDE - - create - make - - feature -- Initialization - - make (a_mime: STRING_8; a_language: STRING_8; a_charset: STRING_8; an_encoding: STRING_8) - - feature -- Compression Negotiation - - encoding_preference (server_encoding_supported: LIST [STRING_8]; header: STRING_8): COMPRESSION_VARIANT_RESULTS - -- server_encoding_supported represent a list of encoding supported by the server. - -- header represent the Accept-Encoding header, ie, the client preferences. - -- Return which Encoding to use in a response, if the server support - -- one Encoding, or empty in other case. - -- Representation: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 - - feature -- Encoding Negotiation - - charset_preference (server_charset_supported: LIST [STRING_8]; header: STRING_8): CHARACTER_ENCODING_VARIANT_RESULTS - -- server_charset_supported represent a list of charset supported by the server. - -- header represent the Accept-Charset header, ie, the client preferences. - -- Return which Charset to use in a response, if the server support - -- one Charset, or empty in other case. - -- Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2 - - feature -- Language Negotiation - - language_preference (server_language_supported: LIST [STRING_8]; header: STRING_8): LANGUAGE_VARIANT_RESULTS - -- server_language_supported represent a list of languages supported by the server. - -- header represent the Accept-Language header, ie, the client preferences. - -- Return which Language to use in a response, if the server support - -- one Language, or empty in other case. - -- Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 - - feature -- Media Type Negotiation - - media_type_preference (mime_types_supported: LIST [STRING_8]; header: STRING_8): MEDIA_TYPE_VARIANT_RESULTS - -- mime_types_supported represent media types supported by the server. - -- header represent the Accept header, ie, the client preferences. - -- Return which media type to use for representaion in a response, if the server support - -- one media type, or empty in other case. - -- Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 - - feature -- Server Side Defaults Formats - - charset_default: STRING_8 - - encoding_default: STRING_8 - - language_default: STRING_8 - - mime_default: STRING_8 - - set_charset_default (a_charset: STRING_8) - -- set the charset_default with `a_charset' - ensure - set_charset: a_charset ~ charset_default - - set_encoding_defautl (an_encoding: STRING_8) - ensure - set_encoding: an_encoding ~ encoding_default - - set_language_default (a_language: STRING_8) - -- set the language_default with `a_language' - ensure - set_language: a_language ~ language_default - - set_mime_default (a_mime: STRING_8) - -- set the mime_default with `a_mime' - ensure - set_mime_default: a_mime ~ mime_default - - note - copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" - - end -- class CONNEG_SERVER_SIDE - From cf01756c1c54fe2677528f094a5963fbf60f79e4 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 18 Oct 2013 20:46:08 +0200 Subject: [PATCH 05/10] Using the new Content Negotiation library to implement WSF_REQUEST.is_content_type_accepted --- library/server/wsf/src/wsf_request.e | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index 299cd912..2659deeb 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -351,31 +351,9 @@ feature -- Helper -- Does client accepts content_type for the response? --| Based on header "Accept:" that can be for instance --| text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 - local - i, j: INTEGER do - if attached http_accept as l_accept then - i := l_accept.substring_index (a_content_type, 1) - if i > 0 then - -- contains the text, now check if this is the exact text - if - i = 1 -- At the beginning of text - or else l_accept[i-1].is_space -- preceded by space - or else l_accept[i-1] = ',' -- preceded by other mime type - then - j := i + a_content_type.count - if l_accept.valid_index (j) then - Result := l_accept[j] = ',' -- followed by other mime type - or else l_accept[j] = ';' -- followed by quality ;q=... - or else l_accept[j].is_space -- followed by space - else -- end of text - Result := True - end - end - end - Result := l_accept.has_substring (a_content_type) - else - Result := True + if attached (create {SERVER_MEDIA_TYPE_NEGOTIATION}.make (a_content_type.as_string_8)).preference (<>, http_accept) as l_variants then + Result := l_variants.is_acceptable end end From 3065637c8096a12e4c4fbeddc6c89edfcef3647f Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 18 Oct 2013 20:59:29 +0200 Subject: [PATCH 06/10] Fixed an issue with one short chunk and empty trailer issue#81 --- .../response/wgi_filter_response.e | 3 +- .../stream/wgi_chunked_input_stream.e | 37 +++++++++++----- .../specification/stream/wgi_input_stream.e | 43 ++++++++++++++----- 3 files changed, 61 insertions(+), 22 deletions(-) diff --git a/library/server/ewsgi/specification/response/wgi_filter_response.e b/library/server/ewsgi/specification/response/wgi_filter_response.e index 7cd9226c..02485387 100644 --- a/library/server/ewsgi/specification/response/wgi_filter_response.e +++ b/library/server/ewsgi/specification/response/wgi_filter_response.e @@ -20,8 +20,9 @@ feature {NONE} -- Initialization res.set_post_commit_action (agent commit) end - wgi_response: WGI_RESPONSE +feature -- Access + wgi_response: WGI_RESPONSE feature {WGI_FILTER_RESPONSE} -- Change diff --git a/library/server/ewsgi/specification/stream/wgi_chunked_input_stream.e b/library/server/ewsgi/specification/stream/wgi_chunked_input_stream.e index 9068ba46..4722591d 100644 --- a/library/server/ewsgi/specification/stream/wgi_chunked_input_stream.e +++ b/library/server/ewsgi/specification/stream/wgi_chunked_input_stream.e @@ -9,6 +9,9 @@ class inherit WGI_INPUT_STREAM + redefine + last_character_available + end create make @@ -36,11 +39,17 @@ feature -- Input index := index + 1 if index > chunk_upper then read_chunk_block - if last_chunk_data = Void then + if + last_chunk_size = 0 + then read_trailer_and_crlf + last_character := '%U' + else + last_character := last_chunk_data.item (index) end + else + last_character := last_chunk_data.item (index) end - last_character := last_chunk_data.item (index) end read_string (nb: INTEGER) @@ -54,7 +63,7 @@ feature -- Input i: like index do last_string.wipe_out - if last_trailer /= Void then + if is_trailer_reached then -- trailer already reached, no more data check input.end_of_input end else @@ -103,6 +112,12 @@ feature -- Access last_character: CHARACTER_8 -- Last item read. + last_character_available: BOOLEAN + -- + do + Result := not is_trailer_reached + end + feature -- Access: chunk last_chunk_size: INTEGER @@ -142,7 +157,13 @@ feature -- Status report end_of_input: BOOLEAN -- Has the end of input stream been reached? do - Result := input.end_of_input + Result := input.end_of_input or is_trailer_reached + end + + is_trailer_reached: BOOLEAN + -- Trailer reached? + do + Result := last_trailer /= Void end feature {NONE} -- Parser @@ -320,11 +341,7 @@ feature {NONE} -- Parser check l_input.last_character = '%N' end end end - if s.is_empty then - last_trailer := Void - else - last_trailer := s - end + last_trailer := s end feature {NONE} -- Implementation @@ -333,7 +350,7 @@ feature {NONE} -- Implementation -- Input Stream ;note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/server/ewsgi/specification/stream/wgi_input_stream.e b/library/server/ewsgi/specification/stream/wgi_input_stream.e index 63f1cb66..4fb778d8 100644 --- a/library/server/ewsgi/specification/stream/wgi_input_stream.e +++ b/library/server/ewsgi/specification/stream/wgi_input_stream.e @@ -62,14 +62,19 @@ feature -- Input i > end_pos loop read_character - a_string.put (last_character, i) - - if end_of_input then - Result := i + if last_character_available then + a_string.put (last_character, i) + if end_of_input then + Result := i + -- Jump out of the loop. + i := end_pos + 1 + else + i := i + 1 + end + else + -- reached end of input -- Jump out of the loop. i := end_pos + 1 - else - i := i + 1 end end if not end_of_input then @@ -107,13 +112,20 @@ feature -- Input i > end_pos loop read_character - a_string.extend (last_character) - l_count := l_count + 1 - if end_of_input then + if last_character_available then + a_string.extend (last_character) + l_count := l_count + 1 + + if end_of_input then + -- Jump out of the loop. + i := end_pos + 1 + else + i := i + 1 + end + else + -- reached end of input -- Jump out of the loop. i := end_pos + 1 - else - i := i + 1 end end last_appended_count := l_count @@ -194,6 +206,15 @@ feature -- Access deferred end + last_character_available: BOOLEAN + -- Is `last_character' available? i.e read? + --| with chunked encoding, we may reach the end and the `last_character' + --| should not be used. + --| to redefine in descendant if needed + do + Result := True + end + last_appended_count: INTEGER -- Count of characters actually read by last `append_to_string' call. From 4f490aaaccab0656964360daf50f7235f5383a12 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 18 Oct 2013 21:01:23 +0200 Subject: [PATCH 07/10] Fixed issue related to {WSF_WGI_DELAYED_HEADER_RESPONSE} and filter response like the logger response wrapper. issue#82 --- library/server/wsf/src/wsf_response.e | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/server/wsf/src/wsf_response.e b/library/server/wsf/src/wsf_response.e index 5ec5c0f6..dbb36855 100644 --- a/library/server/wsf/src/wsf_response.e +++ b/library/server/wsf/src/wsf_response.e @@ -38,12 +38,14 @@ feature {NONE} -- Initialization create header.make wgi_response := r if attached {WSF_WGI_DELAYED_HEADER_RESPONSE} r as r_delayed then - wres := r_delayed - wres.update_wsf_response (Current) + r_delayed.update_wsf_response (Current) + wgi_response := r_delayed + elseif attached {WGI_FILTER_RESPONSE} r as r_filter then + wgi_response := r_filter.wgi_response else create wres.make (r, Current) + wgi_response := wres end - wgi_response := wres set_status_code ({HTTP_STATUS_CODE}.ok) -- Default value end @@ -494,7 +496,7 @@ feature -- Error reporting end note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software From 8c57856232dd0dc8798605b2eb0908d06e18383d Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 18 Oct 2013 21:02:05 +0200 Subject: [PATCH 08/10] Reintroduced WSF_SERVICE.to_wgi_service: WGI_SERVICE since it was used in the "WSF" test cases. --- library/server/wsf/src/service/wsf_service.e | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/server/wsf/src/service/wsf_service.e b/library/server/wsf/src/service/wsf_service.e index e8cb4e0b..38aef9ac 100644 --- a/library/server/wsf/src/service/wsf_service.e +++ b/library/server/wsf/src/service/wsf_service.e @@ -20,6 +20,14 @@ feature -- Execution deferred end +feature -- Conversion + + to_wgi_service: WGI_SERVICE + -- Adapt Current WSF Service to plug into WGI component + do + create {WSF_TO_WGI_SERVICE} Result.make_from_service (Current) + end + note copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" From 464cbcae80d620ad8a4a2a377cc40e6e8aa9f1ac Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 18 Oct 2013 21:02:55 +0200 Subject: [PATCH 09/10] Minor changes in wsf test cases. --- .../server/wsf/tests/src/test_wsf_request.e | 18 ++++++++++++------ .../tests/src/test_wsf_request_mime_handler.e | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/library/server/wsf/tests/src/test_wsf_request.e b/library/server/wsf/tests/src/test_wsf_request.e index 8d086b67..c011f03c 100644 --- a/library/server/wsf/tests/src/test_wsf_request.e +++ b/library/server/wsf/tests/src/test_wsf_request.e @@ -110,9 +110,9 @@ feature {NONE} -- Events create h.make b := base_url if b = Void then - b := "" + b := "/" end - if attached {HTTP_CLIENT_SESSION} h.new_session ("localhost:" + port_number.out + "/" + b) as sess then + if attached {HTTP_CLIENT_SESSION} h.new_session ("localhost:" + port_number.out + b) as sess then http_session := sess sess.set_timeout (-1) sess.set_is_debug (True) @@ -125,10 +125,16 @@ feature {NONE} -- Events do get_http_session if attached http_session as sess then - if attached sess.get (a_url, adapted_context (ctx)) as res and then not res.error_occurred and then attached res.body as l_body then - assert ("Good answer got=%""+l_body+"%" expected=%""+a_expected_body+"%"", l_body.same_string (a_expected_body)) - else - assert ("Request %""+a_url+"%" failed", False) + if attached sess.get (a_url, adapted_context (ctx)) as res then + if attached res.body as l_body then + if res.error_occurred then + assert ("Request %""+a_url+"%" failed, got=[" + l_body + "]", False) + else + assert ("Good answer got=%""+l_body+"%" expected=%""+a_expected_body+"%"", l_body.same_string (a_expected_body)) + end + else + assert ("Request %""+a_url+"%" failed, no body, status=" + res.status.out , False) + end end end end diff --git a/library/server/wsf/tests/src/test_wsf_request_mime_handler.e b/library/server/wsf/tests/src/test_wsf_request_mime_handler.e index 0827fdd7..1231b245 100644 --- a/library/server/wsf/tests/src/test_wsf_request_mime_handler.e +++ b/library/server/wsf/tests/src/test_wsf_request_mime_handler.e @@ -64,7 +64,7 @@ Content-Disposition: form-data; name="password" EWFpassword --__=_the_boundary_1332296477_1804289383_=__-- ]" - + b.replace_substring_all ("%N", "%R%N") h.put_content_length (b.count) --| Case #1 From 6f48cf80f2c8b8eb7d0dedc95eb5ec28b815b736 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 18 Oct 2013 21:04:00 +0200 Subject: [PATCH 10/10] Added content_negotiation in "wsf" library --- library/server/wsf/wsf-safe.ecf | 1 + library/server/wsf/wsf.ecf | 1 + 2 files changed, 2 insertions(+) diff --git a/library/server/wsf/wsf-safe.ecf b/library/server/wsf/wsf-safe.ecf index efeb0034..7cf1b0bf 100644 --- a/library/server/wsf/wsf-safe.ecf +++ b/library/server/wsf/wsf-safe.ecf @@ -15,6 +15,7 @@ + diff --git a/library/server/wsf/wsf.ecf b/library/server/wsf/wsf.ecf index b8088db3..5f0269d8 100644 --- a/library/server/wsf/wsf.ecf +++ b/library/server/wsf/wsf.ecf @@ -15,6 +15,7 @@ +