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
This commit is contained in:
2013-10-18 16:18:22 +02:00
parent d376f99832
commit 88e6837222
25 changed files with 938 additions and 716 deletions

View File

@@ -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

View File

@@ -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
if attached l_target.parameters as l_target_parameters then
across
l_target_parameters as ic
loop
l_element := l_keys.item_for_iteration
l_element := ic.key
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
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
l_keys.forth
end
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

View File

@@ -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
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

View File

@@ -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]))
create Result.make_from_string (a_header)
if Result.parameter ("q") = Void then
Result.put_parameter ("1.0", "q")
end
i := i + 1
end
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)"

View File

@@ -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

View File

@@ -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)
i := a_accept_language_item.index_of (';', 1)
if i > 0 then
put (trimmed_string (p.substring (i + 1, p.count)), trimmed_string (p.substring (1, i - 1)))
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
check is_well_formed_parameter: False end
end
l_parts.forth
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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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")

View File

@@ -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
if attached a_common.parameters as l_parameters then
across
l_parameters as ic
loop
Result.append ("'" + a_common.params.key_for_iteration + "':'" + a_common.params.item_for_iteration + "',");
a_common.params.forth
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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]