Merge branch 'master' of https://github.com/EiffelWebFramework/EWF
This commit is contained in:
@@ -1,125 +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.
|
||||
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.
|
||||
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 type of negotiations (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
|
||||
|
||||
|
||||
@@ -13,6 +13,12 @@
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="..\http\http-safe.ecf" readonly="false"/>
|
||||
<cluster name="conneg" location=".\src\" recursive="true"/>
|
||||
<cluster name="conneg" location=".\src\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/implementation</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
<cluster name="conneg_imp" location=".\src\implementation" recursive="true" hidden="true">
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -13,6 +13,12 @@
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="http" location="../http/http.ecf"/>
|
||||
<cluster name="conneg" location=".\src" recursive="true"/>
|
||||
<cluster name="conneg" location=".\src\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/implementation</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
<cluster name="conneg_imp" location=".\src\implementation" recursive="true" hidden="true">
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -1,230 +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
|
||||
|
||||
SHARED_CONNEG
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (a_mime: READABLE_STRING_8; a_language: READABLE_STRING_8; a_charset: READABLE_STRING_8; a_encoding: READABLE_STRING_8)
|
||||
do
|
||||
set_mime_default (a_mime)
|
||||
set_language_default (a_language)
|
||||
set_charset_default (a_charset)
|
||||
set_encoding_default (a_encoding)
|
||||
ensure
|
||||
mime_default_set: mime_default = a_mime
|
||||
language_default_set: language_default = a_language
|
||||
charset_default_set: charset_default = a_charset
|
||||
encoding_default_set: encoding_default = a_encoding
|
||||
end
|
||||
|
||||
feature -- AccessServer Side Defaults Formats
|
||||
|
||||
mime_default: READABLE_STRING_8
|
||||
-- Media type which is acceptable for the response.
|
||||
|
||||
language_default: READABLE_STRING_8
|
||||
-- Natural language that is preferred as a response to the request.
|
||||
|
||||
charset_default: READABLE_STRING_8
|
||||
-- Character set that is acceptable for the response.
|
||||
|
||||
encoding_default: READABLE_STRING_8
|
||||
-- Content-coding that is acceptable in the response.
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
set_mime_default (a_mime: READABLE_STRING_8)
|
||||
-- Set the mime_default with `a_mime'
|
||||
do
|
||||
mime_default := a_mime
|
||||
ensure
|
||||
mime_default_set: a_mime = mime_default
|
||||
end
|
||||
|
||||
set_language_default (a_language: READABLE_STRING_8)
|
||||
-- Set the language_default with `a_language'
|
||||
do
|
||||
language_default := a_language
|
||||
ensure
|
||||
language_default_set: a_language = language_default
|
||||
end
|
||||
|
||||
set_charset_default (a_charset: READABLE_STRING_8)
|
||||
-- Set the charset_default with `a_charset'
|
||||
do
|
||||
charset_default := a_charset
|
||||
ensure
|
||||
charset_default_set: a_charset = charset_default
|
||||
end
|
||||
|
||||
set_encoding_default (a_encoding: READABLE_STRING_8)
|
||||
do
|
||||
encoding_default := a_encoding
|
||||
ensure
|
||||
encoding_default_set: a_encoding = encoding_default
|
||||
end
|
||||
|
||||
|
||||
|
||||
feature -- Media Type Negotiation
|
||||
|
||||
media_type_preference (a_mime_types_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): MEDIA_TYPE_VARIANT_RESULTS
|
||||
-- `a_mime_types_supported' represent media types supported by the server.
|
||||
-- `a_header represent' the Accept header, ie, the client preferences.
|
||||
-- Return which media type to use for representation in a response, if the server supports
|
||||
-- the requested media type, or empty in other case.
|
||||
note
|
||||
EIS: "name=media type", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri"
|
||||
local
|
||||
l_mime_match: READABLE_STRING_8
|
||||
do
|
||||
create Result
|
||||
if a_header = Void or else a_header.is_empty then
|
||||
-- the request has no Accept header, ie the header is empty, in this case we use the default format
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_type (mime_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
l_mime_match := mime.best_match (a_mime_types_supported, a_header)
|
||||
if l_mime_match.is_empty then
|
||||
-- The server does not support any of the media types preferred by the client
|
||||
Result.set_acceptable (False)
|
||||
Result.set_supported_variants (a_mime_types_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_type (l_mime_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Encoding Negotiation
|
||||
|
||||
charset_preference (a_server_charset_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): CHARACTER_ENCODING_VARIANT_RESULTS
|
||||
-- `a_server_charset_supported' represent a list of character sets supported by the server.
|
||||
-- `a_header' represents the Accept-Charset header, ie, the client preferences.
|
||||
-- Return which Charset to use in a response, if the server supports
|
||||
-- the requested Charset, or empty in other case.
|
||||
note
|
||||
EIS: "name=charset", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2", "protocol=uri"
|
||||
local
|
||||
l_charset_match: READABLE_STRING_8
|
||||
do
|
||||
create Result
|
||||
if a_header = Void or else a_header.is_empty then
|
||||
-- the request has no Accept-Charset header, ie the header is empty, in this case use default charset encoding
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_type (charset_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
l_charset_match := common.best_match (a_server_charset_supported, a_header)
|
||||
if l_charset_match.is_empty then
|
||||
-- The server does not support any of the compression types prefered by the client
|
||||
Result.set_acceptable (False)
|
||||
Result.set_supported_variants (a_server_charset_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_type (l_charset_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Compression Negotiation
|
||||
|
||||
encoding_preference (a_server_encoding_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): COMPRESSION_VARIANT_RESULTS
|
||||
-- `a_server_encoding_supported' represent a list of encoding supported by the server.
|
||||
-- `a_header' represent the Accept-Encoding header, ie, the client preferences.
|
||||
-- Return which Encoding to use in a response, if the server supports
|
||||
-- the requested Encoding, or empty in other case.
|
||||
note
|
||||
EIS: "name=encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri"
|
||||
local
|
||||
l_compression_match: READABLE_STRING_8
|
||||
do
|
||||
create Result
|
||||
if a_header = Void or else a_header.is_empty then
|
||||
-- the request has no Accept-Encoding header, ie the header is empty, in this case do not compress representations
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_type (encoding_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
l_compression_match := common.best_match (a_server_encoding_supported, a_header)
|
||||
if l_compression_match.is_empty then
|
||||
-- The server does not support any of the compression types prefered by the client
|
||||
Result.set_acceptable (False)
|
||||
Result.set_supported_variants (a_server_encoding_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_type (l_compression_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Language Negotiation
|
||||
|
||||
language_preference (a_server_language_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): LANGUAGE_VARIANT_RESULTS
|
||||
-- `a_server_language_supported' represent a list of languages supported by the server.
|
||||
-- `a_header' represent the Accept-Language header, ie, the client preferences.
|
||||
-- Return which Language to use in a response, if the server supports
|
||||
-- the requested Language, or empty in other case.
|
||||
note
|
||||
EIS: "name=language", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4", "protocol=uri"
|
||||
|
||||
local
|
||||
l_language_match: READABLE_STRING_8
|
||||
do
|
||||
create Result
|
||||
if a_header = Void or else a_header.is_empty then
|
||||
-- the request has no Accept header, ie the header is empty, in this case we use the default format
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_type (language_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
l_language_match := language.best_match (a_server_language_supported, a_header)
|
||||
if l_language_match.is_empty then
|
||||
-- The server does not support any of the media types prefered by the client
|
||||
Result.set_acceptable (False)
|
||||
Result.set_supported_variants (a_server_language_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_type (l_language_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -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
|
||||
|
||||
@@ -1,260 +0,0 @@
|
||||
note
|
||||
description: "[
|
||||
COMMON_ACCEPT_HEADER_PARSER, this class allows to parse Accept-Charset and Accept-Encoding headers
|
||||
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=Charset", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2", "protocol=uri"
|
||||
EIS: "name=Encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri"
|
||||
|
||||
class
|
||||
COMMON_ACCEPT_HEADER_PARSER
|
||||
|
||||
inherit {NONE}
|
||||
|
||||
MIME_TYPE_PARSER_UTILITIES
|
||||
|
||||
|
||||
feature -- Parser
|
||||
|
||||
parse_common (header: READABLE_STRING_8): COMMON_RESULTS
|
||||
-- Parses `header' charset/encoding into its component parts.
|
||||
-- For example, the charset 'iso-8889-5' would get parsed
|
||||
-- into:
|
||||
-- ('iso-8889-5', {'q':'1.0'})
|
||||
local
|
||||
l_parts: LIST [READABLE_STRING_8]
|
||||
sub_parts: LIST [READABLE_STRING_8]
|
||||
p: READABLE_STRING_8
|
||||
i: INTEGER
|
||||
l_header: READABLE_STRING_8
|
||||
do
|
||||
create Result.make
|
||||
l_parts := header.split (';')
|
||||
if l_parts.count = 1 then
|
||||
Result.put ("1.0", "q")
|
||||
else
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > l_parts.count
|
||||
loop
|
||||
p := l_parts.at (i)
|
||||
sub_parts := p.split ('=')
|
||||
if sub_parts.count = 2 then
|
||||
Result.put (trim (sub_parts [2]), trim (sub_parts [1]))
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
l_header := trim (l_parts [1])
|
||||
Result.set_field (trim (l_header))
|
||||
end
|
||||
|
||||
fitness_and_quality_parsed (a_field: READABLE_STRING_8; parsed_charsets: LIST [COMMON_RESULTS]): FITNESS_AND_QUALITY
|
||||
-- Find the best match for a given charset/encoding against a list of charsets/encodings
|
||||
-- that have already been parsed by parse_common. Returns a
|
||||
-- tuple of the fitness value and the value of the 'q' quality parameter of
|
||||
-- the best match, or (-1, 0) if no match was found. Just as for
|
||||
-- quality_parsed().
|
||||
local
|
||||
best_fitness: INTEGER
|
||||
target_q: REAL_64
|
||||
best_fit_q: REAL_64
|
||||
target: COMMON_RESULTS
|
||||
range: COMMON_RESULTS
|
||||
element: detachable READABLE_STRING_8
|
||||
l_fitness: INTEGER
|
||||
do
|
||||
best_fitness := -1
|
||||
best_fit_q := 0.0
|
||||
target := parse_common (a_field)
|
||||
if attached target.item ("q") as q and then q.is_double then
|
||||
target_q := q.to_double
|
||||
if target_q < 0.0 then
|
||||
target_q := 0.0
|
||||
elseif target_q > 1.0 then
|
||||
target_q := 1.0
|
||||
end
|
||||
else
|
||||
target_q := 1.0
|
||||
end
|
||||
if attached target.field as l_target_field then
|
||||
from
|
||||
parsed_charsets.start
|
||||
until
|
||||
parsed_charsets.after
|
||||
loop
|
||||
range := parsed_charsets.item_for_iteration
|
||||
if attached range.field as l_range_common then
|
||||
if l_target_field.same_string (l_range_common) or l_target_field.same_string ("*") or l_range_common.same_string ("*")
|
||||
or l_target_field.same_string ("identity") then
|
||||
if l_range_common.same_string (l_target_field) then
|
||||
l_fitness := 100
|
||||
else
|
||||
l_fitness := 0
|
||||
end
|
||||
if l_fitness > best_fitness then
|
||||
best_fitness := l_fitness
|
||||
element := range.item ("q")
|
||||
if element /= Void then
|
||||
best_fit_q := element.to_double.min (target_q)
|
||||
else
|
||||
best_fit_q := 0.0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
parsed_charsets.forth
|
||||
end
|
||||
end
|
||||
create Result.make (best_fitness, best_fit_q)
|
||||
end
|
||||
|
||||
quality_parsed (a_field: READABLE_STRING_8; parsed_common: LIST [COMMON_RESULTS]): REAL_64
|
||||
-- Find the best match for a given charset/encoding against a list of charsets/encodings that
|
||||
-- have already been parsed by parse_charsets(). Returns the 'q' quality
|
||||
-- parameter of the best match, 0 if no match was found. This function
|
||||
-- bahaves the same as quality()
|
||||
do
|
||||
Result := fitness_and_quality_parsed (a_field, parsed_common).quality
|
||||
end
|
||||
|
||||
quality (a_field: READABLE_STRING_8; commons: READABLE_STRING_8): REAL_64
|
||||
-- Returns the quality 'q' of a charset/encoding when compared against the
|
||||
-- a list of charsets/encodings/
|
||||
local
|
||||
l_commons: LIST [READABLE_STRING_8]
|
||||
res: ARRAYED_LIST [COMMON_RESULTS]
|
||||
p_res: COMMON_RESULTS
|
||||
do
|
||||
l_commons := commons.split (',')
|
||||
from
|
||||
create res.make (10);
|
||||
l_commons.start
|
||||
until
|
||||
l_commons.after
|
||||
loop
|
||||
p_res := parse_common (l_commons.item_for_iteration)
|
||||
res.put_left (p_res)
|
||||
l_commons.forth
|
||||
end
|
||||
Result := quality_parsed (a_field, res)
|
||||
end
|
||||
|
||||
best_match (supported: LIST [READABLE_STRING_8]; header: READABLE_STRING_8): READABLE_STRING_8
|
||||
-- Choose the accept with the highest fitness score and quality ('q') from a list of candidates.
|
||||
local
|
||||
l_header_results: LIST [COMMON_RESULTS]
|
||||
weighted_matches: LIST [FITNESS_AND_QUALITY]
|
||||
l_res: LIST [READABLE_STRING_8]
|
||||
p_res: COMMON_RESULTS
|
||||
fitness_and_quality, first_one: detachable FITNESS_AND_QUALITY
|
||||
do
|
||||
l_res := header.split (',')
|
||||
create {ARRAYED_LIST [COMMON_RESULTS]} l_header_results.make (l_res.count)
|
||||
from
|
||||
l_res.start
|
||||
until
|
||||
l_res.after
|
||||
loop
|
||||
p_res := parse_common (l_res.item_for_iteration)
|
||||
l_header_results.force (p_res)
|
||||
l_res.forth
|
||||
end
|
||||
create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (supported.count)
|
||||
from
|
||||
supported.start
|
||||
until
|
||||
supported.after
|
||||
loop
|
||||
fitness_and_quality := fitness_and_quality_parsed (supported.item_for_iteration, l_header_results)
|
||||
fitness_and_quality.set_mime_type (mime_type (supported.item_for_iteration))
|
||||
weighted_matches.force (fitness_and_quality)
|
||||
supported.forth
|
||||
end
|
||||
|
||||
--| Keep only top quality+fitness types
|
||||
--| TODO extract method
|
||||
from
|
||||
weighted_matches.start
|
||||
first_one := weighted_matches.item
|
||||
weighted_matches.forth
|
||||
until
|
||||
weighted_matches.after
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if first_one < fitness_and_quality then
|
||||
first_one := fitness_and_quality
|
||||
if not weighted_matches.isfirst then
|
||||
from
|
||||
weighted_matches.back
|
||||
until
|
||||
weighted_matches.before
|
||||
loop
|
||||
weighted_matches.remove
|
||||
weighted_matches.back
|
||||
end
|
||||
weighted_matches.forth
|
||||
end
|
||||
check
|
||||
weighted_matches.item = fitness_and_quality
|
||||
end
|
||||
weighted_matches.forth
|
||||
elseif first_one.is_equal (fitness_and_quality) then
|
||||
weighted_matches.forth
|
||||
else
|
||||
check
|
||||
first_one > fitness_and_quality
|
||||
end
|
||||
weighted_matches.remove
|
||||
end
|
||||
end
|
||||
if first_one /= Void and then first_one.quality /= 0.0 then
|
||||
if weighted_matches.count = 1 then
|
||||
Result := first_one.mime_type
|
||||
else
|
||||
from
|
||||
fitness_and_quality := Void
|
||||
l_header_results.start
|
||||
until
|
||||
l_header_results.after or fitness_and_quality /= Void
|
||||
loop
|
||||
if attached l_header_results.item.field as l_field then
|
||||
from
|
||||
weighted_matches.start
|
||||
until
|
||||
weighted_matches.after or fitness_and_quality /= Void
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if fitness_and_quality.mime_type.same_string (l_field) then
|
||||
--| Found
|
||||
else
|
||||
fitness_and_quality := Void
|
||||
weighted_matches.forth
|
||||
end
|
||||
end
|
||||
else
|
||||
check
|
||||
has_field: False
|
||||
end
|
||||
end
|
||||
l_header_results.forth
|
||||
end
|
||||
if fitness_and_quality /= Void then
|
||||
Result := fitness_and_quality.mime_type
|
||||
else
|
||||
Result := first_one.mime_type
|
||||
end
|
||||
end
|
||||
else
|
||||
Result := ""
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,256 @@
|
||||
note
|
||||
description: "[
|
||||
{HTTP_ACCEPT_LANGUAGE_UTILITIES} is in charge 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_UTILITIES
|
||||
|
||||
inherit
|
||||
HTTP_HEADER_UTILITIES
|
||||
|
||||
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: 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_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 (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)
|
||||
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_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
|
||||
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
|
||||
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.parameter ("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
|
||||
@@ -1,6 +1,6 @@
|
||||
note
|
||||
description: "[
|
||||
{MIME_PARSE}. 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,26 +20,14 @@
|
||||
EIS: "name=Accept", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri"
|
||||
|
||||
class
|
||||
MIME_PARSE
|
||||
HTTP_ACCEPT_MEDIA_TYPE_UTILITIES
|
||||
|
||||
inherit {NONE}
|
||||
|
||||
MIME_TYPE_PARSER_UTILITIES
|
||||
|
||||
REFACTORING_HELPER
|
||||
inherit
|
||||
HTTP_HEADER_UTILITIES
|
||||
|
||||
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 +36,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,111 +56,6 @@ feature -- Parser
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
fitness_and_quality_parsed (a_mime_type: READABLE_STRING_8; parsed_ranges: LIST [HTTP_MEDIA_TYPE]): FITNESS_AND_QUALITY
|
||||
-- Find the best match for a given mimeType against a list of media_ranges
|
||||
-- that have already been parsed by parse_media_range.
|
||||
local
|
||||
best_fitness: INTEGER
|
||||
target_q: REAL_64
|
||||
best_fit_q: REAL_64
|
||||
target: HTTP_MEDIA_TYPE
|
||||
range: HTTP_MEDIA_TYPE
|
||||
param_matches: INTEGER
|
||||
element: detachable READABLE_STRING_8
|
||||
l_fitness: INTEGER
|
||||
do
|
||||
best_fitness := -1
|
||||
best_fit_q := 0.0
|
||||
target := parse_media_range (a_mime_type)
|
||||
if attached target.parameter ("q") as q and then q.is_double then
|
||||
target_q := q.to_double
|
||||
if target_q < 0.0 then
|
||||
target_q := 0.0
|
||||
elseif target_q > 1.0 then
|
||||
target_q := 1.0
|
||||
end
|
||||
else
|
||||
target_q := 1.0
|
||||
end
|
||||
|
||||
if
|
||||
attached target.type as l_target_type and
|
||||
attached target.subtype as l_target_sub_type
|
||||
then
|
||||
from
|
||||
parsed_ranges.start
|
||||
until
|
||||
parsed_ranges.after
|
||||
loop
|
||||
range := parsed_ranges.item_for_iteration
|
||||
if
|
||||
(
|
||||
attached range.type as l_range_type and then
|
||||
(l_target_type.same_string (l_range_type) or l_range_type.same_string ("*") or l_target_type.same_string ("*"))
|
||||
) and
|
||||
(
|
||||
attached range.subtype as l_range_sub_type and then
|
||||
(l_target_sub_type.same_string (l_range_sub_type) or l_range_sub_type.same_string ("*") or l_target_sub_type.same_string ("*"))
|
||||
)
|
||||
then
|
||||
if attached target.parameters as l_keys then
|
||||
from
|
||||
param_matches := 0
|
||||
l_keys.start
|
||||
until
|
||||
l_keys.after
|
||||
loop
|
||||
element := l_keys.key_for_iteration
|
||||
if
|
||||
not element.same_string ("q") and then
|
||||
range.has_parameter (element) and then
|
||||
(attached target.parameter (element) as t_item and attached range.parameter (element) as r_item) and then
|
||||
t_item.same_string (r_item)
|
||||
then
|
||||
param_matches := param_matches + 1
|
||||
end
|
||||
l_keys.forth
|
||||
end
|
||||
end
|
||||
if l_range_type.same_string (l_target_type) then
|
||||
l_fitness := 100
|
||||
else
|
||||
l_fitness := 0
|
||||
end
|
||||
|
||||
if l_range_sub_type.same_string (l_target_sub_type) then
|
||||
l_fitness := l_fitness + 10
|
||||
end
|
||||
|
||||
l_fitness := l_fitness + param_matches
|
||||
|
||||
if l_fitness > best_fitness then
|
||||
best_fitness := l_fitness
|
||||
element := range.parameter ("q")
|
||||
if element /= Void then
|
||||
best_fit_q := element.to_double.min (target_q)
|
||||
else
|
||||
best_fit_q := 0.0
|
||||
end
|
||||
end
|
||||
end
|
||||
parsed_ranges.forth
|
||||
end
|
||||
end
|
||||
create Result.make (best_fitness, best_fit_q)
|
||||
end
|
||||
|
||||
quality_parsed (a_mime_type: READABLE_STRING_8; parsed_ranges: LIST [HTTP_MEDIA_TYPE]): REAL_64
|
||||
-- Find the best match for a given mime-type against a list of ranges that
|
||||
-- have already been parsed by parse_media_range. Returns the 'q' quality
|
||||
-- parameter of the best match, 0 if no match was found. This function
|
||||
-- bahaves the same as quality except that 'parsed_ranges' must be a list
|
||||
-- of parsed media ranges.
|
||||
do
|
||||
Result := fitness_and_quality_parsed (a_mime_type, parsed_ranges).quality
|
||||
end
|
||||
|
||||
quality (a_mime_type: READABLE_STRING_8; ranges: READABLE_STRING_8): REAL_64
|
||||
-- Returns the quality 'q' of a mime-type when compared against the
|
||||
-- mediaRanges in ranges.
|
||||
@@ -188,14 +71,14 @@ feature -- Parser
|
||||
until
|
||||
l_ranges.after
|
||||
loop
|
||||
p_res := parse_media_range (l_ranges.item_for_iteration)
|
||||
res.put_left (p_res)
|
||||
p_res := media_type (l_ranges.item_for_iteration)
|
||||
res.force (p_res)
|
||||
l_ranges.forth
|
||||
end
|
||||
Result := quality_parsed (a_mime_type, res)
|
||||
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]
|
||||
@@ -214,22 +97,17 @@ feature -- Parser
|
||||
until
|
||||
l_res.after
|
||||
loop
|
||||
p_res := parse_media_range (l_res.item_for_iteration)
|
||||
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)
|
||||
create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (0)
|
||||
|
||||
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))
|
||||
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
|
||||
@@ -265,7 +143,7 @@ feature -- Parser
|
||||
end
|
||||
if first_one /= Void and then first_one.quality /= 0.0 then
|
||||
if weighted_matches.count = 1 then
|
||||
Result := first_one.mime_type
|
||||
Result := first_one.entity
|
||||
else
|
||||
from
|
||||
fitness_and_quality := Void
|
||||
@@ -280,7 +158,7 @@ feature -- Parser
|
||||
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
|
||||
if fitness_and_quality.entity.same_string (s) then
|
||||
--| Found
|
||||
else
|
||||
fitness_and_quality := Void
|
||||
@@ -290,9 +168,9 @@ feature -- Parser
|
||||
l_header_results.forth
|
||||
end
|
||||
if fitness_and_quality /= Void then
|
||||
Result := fitness_and_quality.mime_type
|
||||
Result := fitness_and_quality.entity
|
||||
else
|
||||
Result := first_one.mime_type
|
||||
Result := first_one.entity
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -300,6 +178,108 @@ feature -- Parser
|
||||
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
|
||||
best_fitness: INTEGER
|
||||
target_q: REAL_64
|
||||
best_fit_q: REAL_64
|
||||
target: HTTP_MEDIA_TYPE
|
||||
range: HTTP_MEDIA_TYPE
|
||||
param_matches: INTEGER
|
||||
element: detachable READABLE_STRING_8
|
||||
l_fitness: INTEGER
|
||||
do
|
||||
best_fitness := -1
|
||||
best_fit_q := 0.0
|
||||
target := 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
|
||||
target_q := 0.0
|
||||
elseif target_q > 1.0 then
|
||||
target_q := 1.0
|
||||
end
|
||||
else
|
||||
target_q := 1.0
|
||||
end
|
||||
|
||||
if
|
||||
attached target.type as l_target_type and
|
||||
attached target.subtype as l_target_sub_type
|
||||
then
|
||||
from
|
||||
parsed_ranges.start
|
||||
until
|
||||
parsed_ranges.after
|
||||
loop
|
||||
range := parsed_ranges.item_for_iteration
|
||||
if
|
||||
(
|
||||
attached range.type as l_range_type and then
|
||||
(l_target_type.same_string (l_range_type) or l_range_type.same_string ("*") or l_target_type.same_string ("*"))
|
||||
) and
|
||||
(
|
||||
attached range.subtype as l_range_sub_type and then
|
||||
(l_target_sub_type.same_string (l_range_sub_type) or l_range_sub_type.same_string ("*") or l_target_sub_type.same_string ("*"))
|
||||
)
|
||||
then
|
||||
if attached target.parameters as l_keys then
|
||||
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
|
||||
(attached target.parameter (element) as t_item and attached range.parameter (element) as r_item) and then
|
||||
t_item.same_string (r_item)
|
||||
then
|
||||
param_matches := param_matches + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if l_range_type.same_string (l_target_type) then
|
||||
l_fitness := 100
|
||||
else
|
||||
l_fitness := 0
|
||||
end
|
||||
|
||||
if l_range_sub_type.same_string (l_target_sub_type) then
|
||||
l_fitness := l_fitness + 10
|
||||
end
|
||||
|
||||
l_fitness := l_fitness + param_matches
|
||||
|
||||
if l_fitness > best_fitness then
|
||||
best_fitness := l_fitness
|
||||
element := range.parameter ("q")
|
||||
if element /= Void then
|
||||
best_fit_q := element.to_double.min (target_q)
|
||||
else
|
||||
best_fit_q := 0.0
|
||||
end
|
||||
end
|
||||
end
|
||||
parsed_ranges.forth
|
||||
end
|
||||
end
|
||||
create Result.make (best_fitness, best_fit_q)
|
||||
end
|
||||
|
||||
quality_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_from_list (a_mime_type, 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)"
|
||||
@@ -0,0 +1,237 @@
|
||||
note
|
||||
description: "[
|
||||
HTTP_ANY_ACCEPT_HEADER_PARSER, this class allows to parse Accept-* headers
|
||||
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=Charset", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2", "protocol=uri"
|
||||
EIS: "name=Encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri"
|
||||
|
||||
class
|
||||
HTTP_ANY_ACCEPT_HEADER_UTILITIES
|
||||
|
||||
inherit
|
||||
HTTP_HEADER_UTILITIES
|
||||
|
||||
feature -- Parser
|
||||
|
||||
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'})
|
||||
do
|
||||
create Result.make_from_string (a_header)
|
||||
if Result.parameter ("q") = Void then
|
||||
Result.put_parameter ("1.0", "q")
|
||||
end
|
||||
end
|
||||
|
||||
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]
|
||||
p_res: HTTP_ANY_ACCEPT
|
||||
do
|
||||
l_commons := a_header.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: 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]
|
||||
l_weighted_matches: LIST [FITNESS_AND_QUALITY]
|
||||
l_res: LIST [READABLE_STRING_8]
|
||||
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]} 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 (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)
|
||||
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.value 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]): FITNESS_AND_QUALITY
|
||||
-- Find the best match for a given charset/encoding against a list of charsets/encodings
|
||||
-- that have already been parsed by parse_common. Returns a
|
||||
-- tuple of the fitness value and the value of the 'q' quality parameter of
|
||||
-- the best match, or (-1, 0) if no match was found. Just as for
|
||||
-- quality_parsed().
|
||||
local
|
||||
best_fitness: INTEGER
|
||||
target_q: REAL_64
|
||||
best_fit_q: REAL_64
|
||||
target: 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.parameter ("q") as q and then q.is_double then
|
||||
target_q := q.to_double
|
||||
if target_q < 0.0 then
|
||||
target_q := 0.0
|
||||
elseif target_q > 1.0 then
|
||||
target_q := 1.0
|
||||
end
|
||||
else
|
||||
target_q := 1.0
|
||||
end
|
||||
if attached target.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.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 ("*")
|
||||
or l_range_common.same_string ("identity")
|
||||
then
|
||||
if l_range_common.same_string (l_target_field) then
|
||||
l_fitness := 100
|
||||
else
|
||||
l_fitness := 0
|
||||
end
|
||||
if l_fitness > best_fitness then
|
||||
best_fitness := l_fitness
|
||||
element := range.parameter ("q")
|
||||
if element /= Void then
|
||||
best_fit_q := element.to_double.min (target_q)
|
||||
else
|
||||
best_fit_q := 0.0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
a_parsed_charsets.forth
|
||||
end
|
||||
end
|
||||
create Result.make (best_fitness, best_fit_q)
|
||||
end
|
||||
|
||||
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
|
||||
-- bahaves the same as quality()
|
||||
do
|
||||
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)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,44 @@
|
||||
note
|
||||
description: "Summary description for {HTTP_HEADER_UTILITIES}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
HTTP_HEADER_UTILITIES
|
||||
|
||||
inherit
|
||||
REFACTORING_HELPER
|
||||
|
||||
feature {NONE} -- Helpers
|
||||
|
||||
entity_value (a_str: READABLE_STRING_8): READABLE_STRING_8
|
||||
-- `s' with any trailing parameters stripped
|
||||
local
|
||||
p: INTEGER
|
||||
do
|
||||
p := a_str.index_of (';', 1)
|
||||
if p > 0 then
|
||||
Result := trimmed_string (a_str.substring (1, p - 1))
|
||||
else
|
||||
Result := trimmed_string (a_str.string)
|
||||
end
|
||||
end
|
||||
|
||||
trimmed_string (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
|
||||
do
|
||||
create Result.make_from_string (a_string)
|
||||
Result.left_adjust
|
||||
Result.right_adjust
|
||||
ensure
|
||||
result_trimmed: a_string.has_substring (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
|
||||
@@ -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.put_left (p_res)
|
||||
l_ranges.forth
|
||||
end
|
||||
Result := quality_parsed (a_language, res)
|
||||
end
|
||||
|
||||
best_match (a_supported: LIST [READABLE_STRING_8]; a_header: READABLE_STRING_8): READABLE_STRING_8
|
||||
-- Choose the `language' with the highest fitness score and quality ('q') from a list of candidates.
|
||||
local
|
||||
l_header_results: LIST [LANGUAGE_RESULTS]
|
||||
weighted_matches: LIST [FITNESS_AND_QUALITY]
|
||||
l_res: LIST [READABLE_STRING_8]
|
||||
p_res: LANGUAGE_RESULTS
|
||||
fitness_and_quality, first_one: detachable FITNESS_AND_QUALITY
|
||||
s: READABLE_STRING_8
|
||||
do
|
||||
l_res := a_header.split (',')
|
||||
create {ARRAYED_LIST [LANGUAGE_RESULTS]} l_header_results.make (l_res.count)
|
||||
fixme ("Extract method!!!")
|
||||
from
|
||||
l_res.start
|
||||
until
|
||||
l_res.after
|
||||
loop
|
||||
p_res := parse_language_range (l_res.item_for_iteration)
|
||||
l_header_results.force (p_res)
|
||||
l_res.forth
|
||||
end
|
||||
create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (a_supported.count)
|
||||
from
|
||||
a_supported.start
|
||||
until
|
||||
a_supported.after
|
||||
loop
|
||||
fitness_and_quality := fitness_and_quality_parsed (a_supported.item_for_iteration, l_header_results)
|
||||
fitness_and_quality.set_mime_type (mime_type (a_supported.item_for_iteration))
|
||||
weighted_matches.force (fitness_and_quality)
|
||||
a_supported.forth
|
||||
end
|
||||
|
||||
--| Keep only top quality+fitness types
|
||||
from
|
||||
weighted_matches.start
|
||||
first_one := weighted_matches.item
|
||||
weighted_matches.forth
|
||||
until
|
||||
weighted_matches.after
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if first_one < fitness_and_quality then
|
||||
first_one := fitness_and_quality
|
||||
if not weighted_matches.isfirst then
|
||||
from
|
||||
weighted_matches.back
|
||||
until
|
||||
weighted_matches.before
|
||||
loop
|
||||
weighted_matches.remove
|
||||
weighted_matches.back
|
||||
end
|
||||
weighted_matches.forth
|
||||
end
|
||||
check
|
||||
weighted_matches.item = fitness_and_quality
|
||||
end
|
||||
weighted_matches.forth
|
||||
elseif first_one.is_equal (fitness_and_quality) then
|
||||
weighted_matches.forth
|
||||
else
|
||||
check
|
||||
first_one > fitness_and_quality
|
||||
end
|
||||
weighted_matches.remove
|
||||
end
|
||||
end
|
||||
if first_one /= Void and then first_one.quality /= 0.0 then
|
||||
if weighted_matches.count = 1 then
|
||||
Result := first_one.mime_type
|
||||
else
|
||||
from
|
||||
fitness_and_quality := Void
|
||||
l_header_results.start
|
||||
until
|
||||
l_header_results.after or fitness_and_quality /= Void
|
||||
loop
|
||||
s := l_header_results.item.mime_type
|
||||
from
|
||||
weighted_matches.start
|
||||
until
|
||||
weighted_matches.after or fitness_and_quality /= Void
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if fitness_and_quality.mime_type.same_string (s) then
|
||||
--| Found
|
||||
else
|
||||
fitness_and_quality := Void
|
||||
weighted_matches.forth
|
||||
end
|
||||
end
|
||||
l_header_results.forth
|
||||
end
|
||||
if fitness_and_quality /= Void then
|
||||
Result := fitness_and_quality.mime_type
|
||||
else
|
||||
Result := first_one.mime_type
|
||||
end
|
||||
end
|
||||
else
|
||||
Result := ""
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -1,43 +0,0 @@
|
||||
note
|
||||
description: "{MIME_TYPE_PARSER_UTILITIES}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
MIME_TYPE_PARSER_UTILITIES
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
mime_type (a_str: READABLE_STRING_8): READABLE_STRING_8
|
||||
-- `s' with any trailing parameters stripped
|
||||
local
|
||||
p: INTEGER
|
||||
do
|
||||
p := a_str.index_of (';', 1)
|
||||
if p > 0 then
|
||||
Result := trim (a_str.substring (1, p - 1))
|
||||
else
|
||||
Result := trim (a_str.string)
|
||||
end
|
||||
end
|
||||
|
||||
trim (a_string: READABLE_STRING_8): READABLE_STRING_8
|
||||
-- trim whitespace from the beginning and end of a string
|
||||
-- `a_string'
|
||||
require
|
||||
valid_argument : a_string /= Void
|
||||
local
|
||||
l_result: STRING
|
||||
do
|
||||
l_result := a_string.as_string_8
|
||||
l_result.left_adjust
|
||||
l_result.right_adjust
|
||||
Result := l_result
|
||||
ensure
|
||||
result_same_as_argument: Result.same_string_general (a_string)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -1,90 +0,0 @@
|
||||
note
|
||||
description: "Object that represents a results after parsing Charset or Encoding Accept headers."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
COMMON_RESULTS
|
||||
|
||||
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]
|
||||
--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_field (a_field: STRING)
|
||||
-- Set type with `a_charset'
|
||||
do
|
||||
field := a_field
|
||||
ensure
|
||||
field_set: attached field as l_field implies l_field = a_field
|
||||
end
|
||||
|
||||
put (new: STRING; key: STRING)
|
||||
-- Insert `new' with `key' if there is no other item
|
||||
-- associated with the same key. If present, replace
|
||||
-- the old value with `new'
|
||||
do
|
||||
if params.has_key (key) then
|
||||
params.replace (new, key)
|
||||
else
|
||||
params.force (new, key)
|
||||
end
|
||||
ensure
|
||||
has_key: params.has_key (key)
|
||||
has_item: params.has_item (new)
|
||||
end
|
||||
|
||||
feature -- Status Report
|
||||
|
||||
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
|
||||
@@ -0,0 +1,242 @@
|
||||
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.
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
fixme (generator + ".make_from_string: improve code!!!")
|
||||
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 parameter ("q") as q then
|
||||
if q.same_string ("1") then
|
||||
--| Use 1.0 formatting
|
||||
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_parameter ("1.0", "q")
|
||||
quality := 1.0
|
||||
end
|
||||
else
|
||||
put_parameter ("1.0", "q")
|
||||
end
|
||||
end
|
||||
|
||||
make_with_language (a_lang_tag: READABLE_STRING_8)
|
||||
-- Instantiate Current from language tag `a_lang_tag'.
|
||||
do
|
||||
initialize
|
||||
set_language_range (a_lang_tag)
|
||||
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 parameters.make (1)
|
||||
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 -- 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
|
||||
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
|
||||
|
||||
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 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
|
||||
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
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,31 +0,0 @@
|
||||
note
|
||||
description: "Summary description for {SHARED_CONNEG}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
SHARED_CONNEG
|
||||
|
||||
feature
|
||||
|
||||
Mime: MIME_PARSE
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
Common: COMMON_ACCEPT_HEADER_PARSER
|
||||
-- Charset and Encoding
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
Language: LANGUAGE_PARSE
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 ITERABLE [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: ITERABLE [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
|
||||
@@ -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
|
||||
@@ -16,10 +16,10 @@ feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
local
|
||||
mime_parse : MIME_PARSE
|
||||
mime_parse : HTTP_ACCEPT_MEDIA_TYPE_UTILITIES
|
||||
accept : STRING
|
||||
charset_parse : COMMON_ACCEPT_HEADER_PARSER
|
||||
language : LANGUAGE_PARSE
|
||||
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")
|
||||
@@ -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
|
||||
|
||||
@@ -23,33 +23,31 @@ feature {NONE} -- Events
|
||||
|
||||
feature -- Helpers
|
||||
|
||||
format (a_common: COMMON_RESULTS): 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
|
||||
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 +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 : COMMON_ACCEPT_HEADER_PARSER
|
||||
parser : HTTP_ANY_ACCEPT_HEADER_UTILITIES
|
||||
|
||||
end
|
||||
|
||||
@@ -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
|
||||
@@ -33,24 +33,28 @@ 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)
|
||||
assert ("Variant header is void",media_variants.variant_header = Void)
|
||||
assert ("Media type is void",media_variants.type = Void)
|
||||
if attached media_variants.supported_variants as l_supported_variants then
|
||||
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
|
||||
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 defaul", conneg.mime_default.is_equal (media_variants.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", media_variants.variant_header.is_equal ("Accept"))
|
||||
assert ("Media Type is application/json", media_variants.type.is_equal ("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
|
||||
|
||||
@@ -58,40 +62,44 @@ 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)
|
||||
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)
|
||||
assert ("Variant header is void",charset_variants.variant_header = Void)
|
||||
assert ("Character type is void",charset_variants.type = Void)
|
||||
if attached charset_variants.supported_variants as l_supported_variants then
|
||||
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
|
||||
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", conneg.charset_default.is_equal (charset_variants.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", charset_variants.variant_header.is_equal ("Accept-Charset"))
|
||||
assert ("Character Type is iso-8859-5", charset_variants.type.is_equal ("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
|
||||
@@ -100,91 +108,90 @@ 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)
|
||||
assert ("Variant header is void",compression_variants.variant_header = Void)
|
||||
assert ("Compression type is void",compression_variants.type = Void)
|
||||
if attached compression_variants.supported_variants as l_supported_variants then
|
||||
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
|
||||
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", conneg.encoding_default.is_equal (compression_variants.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", compression_variants.variant_header.is_equal ("Accept-Encoding"))
|
||||
assert ("Encoding Type is gzip", compression_variants.type.is_equal ("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"))
|
||||
|
||||
-- Scenario 4, the server set `identity' and the client doesn't mention identity
|
||||
l_compression := "identity"
|
||||
compression_supported := l_compression.split(',')
|
||||
conneg.set_encoding_default("gzip")
|
||||
conneg.set_default_encoding ("gzip")
|
||||
compression_variants := conneg.encoding_preference (compression_supported, "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 identity", compression_variants.type.is_equal ("identity"))
|
||||
assert ("Expected Not Acceptable", not compression_variants.is_acceptable)
|
||||
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 Void", compression_variants.encoding = Void)
|
||||
|
||||
-- Scenario 5, the server set `identity' and the client mention identity,q=0
|
||||
l_compression := "identity"
|
||||
compression_supported := l_compression.split(',')
|
||||
conneg.set_encoding_default("gzip")
|
||||
conneg.set_default_encoding ("gzip")
|
||||
compression_variants := conneg.encoding_preference (compression_supported, "identity;q=0")
|
||||
assert ("Expected Not Acceptable", not compression_variants.is_acceptable)
|
||||
assert ("Variants is attached",attached compression_variants.supported_variants )
|
||||
assert ("Variant Header is void", compression_variants.variant_header = Void)
|
||||
assert ("Encoding 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 ("Encoding Type is Void", compression_variants.encoding = Void)
|
||||
|
||||
-- Scenario 6, the server set `identity' and the client mention *,q=0
|
||||
l_compression := "identity"
|
||||
compression_supported := l_compression.split(',')
|
||||
conneg.set_encoding_default("gzip")
|
||||
conneg.set_default_encoding ("gzip")
|
||||
compression_variants := conneg.encoding_preference (compression_supported, "*;q=0")
|
||||
assert ("Expected Not Acceptable", not compression_variants.is_acceptable)
|
||||
assert ("Variants is attached",attached compression_variants.supported_variants )
|
||||
assert ("Variant Header is void", compression_variants.variant_header = Void)
|
||||
assert ("Encoding 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 ("Encoding Type is Void", compression_variants.encoding = Void)
|
||||
|
||||
|
||||
-- Scenario 7, the server set `identity' and the client mention identity;q=0.5, gzip;q=0.7,compress
|
||||
l_compression := "identity"
|
||||
compression_supported := l_compression.split(',')
|
||||
conneg.set_encoding_default("gzip")
|
||||
conneg.set_default_encoding ("gzip")
|
||||
compression_variants := conneg.encoding_preference (compression_supported, "identity;q=0.5, gzip;q=0.7,compress")
|
||||
assert ("Expected Acceptable",compression_variants.is_acceptable)
|
||||
assert ("Variants is void",compression_variants.supported_variants = Void)
|
||||
assert ("Variant Header", compression_variants.variant_header.is_equal ("Accept-Encoding"))
|
||||
assert ("Encoding Type is identity", compression_variants.type.is_equal ("identity"))
|
||||
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 identity", attached compression_variants.encoding as l_type and then l_type.same_string ("identity"))
|
||||
|
||||
|
||||
-- Scenario 8, the server set `identity' and the client mention identity;q=0.5
|
||||
l_compression := "identity"
|
||||
compression_supported := l_compression.split(',')
|
||||
conneg.set_encoding_default("gzip")
|
||||
conneg.set_default_encoding ("gzip")
|
||||
compression_variants := conneg.encoding_preference (compression_supported, "identity;q=0.5")
|
||||
assert ("Expected Acceptable",compression_variants.is_acceptable)
|
||||
assert ("Variants is void",compression_variants.supported_variants = Void)
|
||||
assert ("Variant Header", compression_variants.variant_header.is_equal ("Accept-Encoding"))
|
||||
assert ("Encoding Type is identity", compression_variants.type.is_equal ("identity"))
|
||||
|
||||
|
||||
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 identity", attached compression_variants.encoding as l_type and then l_type.same_string ("identity"))
|
||||
end
|
||||
|
||||
|
||||
|
||||
test_language_negotiation
|
||||
local
|
||||
language_variants : LANGUAGE_VARIANT_RESULTS
|
||||
language_variants : HTTP_ACCEPT_LANGUAGE_VARIANTS
|
||||
languages_supported : LIST [STRING]
|
||||
l_languages : STRING
|
||||
do
|
||||
@@ -193,30 +200,61 @@ 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)
|
||||
|
||||
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", 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
|
||||
|
||||
-- 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 ("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", language_variants.variant_header.is_equal ("Accept-Language"))
|
||||
assert ("Language Type is fr", language_variants.type.is_equal ("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
|
||||
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
|
||||
|
||||
@@ -23,25 +23,22 @@ 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 (" {")
|
||||
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 ("})")
|
||||
@@ -52,10 +49,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 +70,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 +134,7 @@ feature -- Test routines
|
||||
|
||||
|
||||
|
||||
parser : LANGUAGE_PARSE
|
||||
parser : HTTP_ACCEPT_LANGUAGE_UTILITIES
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -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 ("})")
|
||||
@@ -54,15 +51,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 +145,7 @@ feature -- Test routines
|
||||
|
||||
|
||||
|
||||
parser : MIME_PARSE
|
||||
parser : HTTP_ACCEPT_MEDIA_TYPE_UTILITIES
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-6-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-6-0 http://www.eiffel.com/developers/xml/configuration-1-6-0.xsd" name="test" uuid="7860561C-779A-4E45-A7B9-06A1E0E984E8">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="test" uuid="7860561C-779A-4E45-A7B9-06A1E0E984E8">
|
||||
<target name="test">
|
||||
<root class="APPLICATION" feature="make"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
<exclude>/.git$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true">
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="conneg" location="..\conneg-safe.ecf" readonly="false">
|
||||
<option>
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
</library>
|
||||
<library name="http" location="..\..\http\http-safe.ecf" readonly="false"/>
|
||||
<library name="conneg" location="..\conneg-safe.ecf"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||
<cluster name="test" location=".\" recursive="true"/>
|
||||
</target>
|
||||
|
||||
@@ -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
|
||||
|
||||
150
library/network/protocol/http/src/http_parameter_table.e
Normal file
150
library/network/protocol/http/src/http_parameter_table.e
Normal 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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
-- <Precursor>
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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 (<<a_content_type.as_string_8>>, http_accept) as l_variants then
|
||||
Result := l_variants.is_acceptable
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<library name="error" location="..\..\utility\general\error\error-safe.ecf"/>
|
||||
<library name="ewsgi" location="..\ewsgi\ewsgi-safe.ecf"/>
|
||||
<library name="http" location="..\..\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="conneg" location="..\..\network\protocol\content_negotiation\conneg-safe.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||
<library name="uri_template" location="..\..\text\parser\uri_template\uri_template-safe.ecf"/>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<library name="error" location="../../utility/general/error/error.ecf"/>
|
||||
<library name="http" location="../../network/protocol/http/http.ecf"/>
|
||||
<library name="conneg" location="..\..\network\protocol\content_negotiation\conneg.ecf"/>
|
||||
<library name="uri_template"
|
||||
location="../../text/parser/uri_template/uri_template.ecf"/>
|
||||
<library name="encoder"
|
||||
|
||||
Reference in New Issue
Block a user