diff --git a/library/protocol/CONNEG/.gitignore b/library/protocol/CONNEG/.gitignore
new file mode 100644
index 00000000..a7f9c034
--- /dev/null
+++ b/library/protocol/CONNEG/.gitignore
@@ -0,0 +1 @@
+EIFGENs/
diff --git a/library/protocol/CONNEG/README.md b/library/protocol/CONNEG/README.md
new file mode 100644
index 00000000..c368f653
--- /dev/null
+++ b/library/protocol/CONNEG/README.md
@@ -0,0 +1,5 @@
+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
diff --git a/library/protocol/CONNEG/library/.gitignore b/library/protocol/CONNEG/library/.gitignore
new file mode 100644
index 00000000..ac53b3c2
--- /dev/null
+++ b/library/protocol/CONNEG/library/.gitignore
@@ -0,0 +1,4 @@
+*~
+EIFGEN* # ignore all files in the EIFGENs/ directory
+
+
diff --git a/library/protocol/CONNEG/library/common_accept_header_parser.e b/library/protocol/CONNEG/library/common_accept_header_parser.e
new file mode 100644
index 00000000..f95e6751
--- /dev/null
+++ b/library/protocol/CONNEG/library/common_accept_header_parser.e
@@ -0,0 +1,282 @@
+note
+ description: "COMMON_ACCEPT_HEADER_PARSER, this class allows to parse Accept-Charset and Accept-Encoding headers"
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+ description : "[
+ Charset Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2
+ Encoding Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
+ ]"
+
+class
+ COMMON_ACCEPT_HEADER_PARSER
+
+
+feature -- Parser
+ parse_common (header: STRING): 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 [STRING]
+ sub_parts: LIST [STRING]
+ p: STRING
+ i: INTEGER
+ l_header: STRING
+ 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: STRING; 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 STRING
+ 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 ("*") 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: STRING; 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: STRING; commons: STRING): REAL_64
+ -- Returns the quality 'q' of a charset/encoding when compared against the
+ -- a list of charsets/encodings/
+ local
+ l_commons : LIST [STRING]
+ 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 [STRING]; header: STRING): STRING
+ -- 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 [STRING]
+ 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
+
+feature -- Util
+
+ mime_type (s: STRING): STRING
+ local
+ p: INTEGER
+ do
+ p := s.index_of (';', 1)
+ if p > 0 then
+ Result := trim (s.substring (1, p - 1))
+ else
+ Result := trim (s.string)
+ end
+ end
+
+ trim (a_string: STRING): STRING
+ -- trim whitespace from the beginning and end of a string
+ require
+ valid_argument : a_string /= Void
+ do
+ a_string.left_adjust
+ a_string.right_justify
+ Result := a_string
+ ensure
+ result_same_as_argument: a_string = Result
+ end
+
+note
+ copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+end
diff --git a/library/protocol/CONNEG/library/common_results.e b/library/protocol/CONNEG/library/common_results.e
new file mode 100644
index 00000000..5d71a733
--- /dev/null
+++ b/library/protocol/CONNEG/library/common_results.e
@@ -0,0 +1,119 @@
+note
+ description: "Summary description for {COMMON_RESULTS}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ COMMON_RESULTS
+inherit
+ ANY
+ redefine
+ out
+ end
+
+ DEBUG_OUTPUT
+ redefine
+ out
+ end
+
+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
+
+ 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_assigned: field ~ 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
+
+ out: STRING
+ -- Representation of the current object
+ do
+ create Result.make_from_string ("(")
+ if attached field as t then
+ Result.append_string ("'" + t + "',")
+ end
+ Result.append_string (" {")
+
+ from
+ params.start
+ until
+ params.after
+ loop
+ Result.append ("'" + params.key_for_iteration + "':'" + params.item_for_iteration + "',");
+ params.forth
+ end
+ Result.append ("})")
+ end
+
+ debug_output: STRING
+ -- String that should be displayed in debugger to represent `Current'.
+ do
+ Result := out
+ end
+
+feature {NONE} -- Implementation
+
+ params: HASH_TABLE [STRING, STRING]
+ --dictionary of all the parameters for the media range
+
+;note
+ copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+end
diff --git a/library/protocol/CONNEG/library/conneg-safe.ecf b/library/protocol/CONNEG/library/conneg-safe.ecf
new file mode 100644
index 00000000..cb94e049
--- /dev/null
+++ b/library/protocol/CONNEG/library/conneg-safe.ecf
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+ /.git$
+ /EIFGENs$
+ /CVS$
+ /.svn$
+
+
+
+
diff --git a/library/protocol/CONNEG/library/conneg.ecf b/library/protocol/CONNEG/library/conneg.ecf
new file mode 100644
index 00000000..258bd52d
--- /dev/null
+++ b/library/protocol/CONNEG/library/conneg.ecf
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+ /EIFGENs$
+ /CVS$
+ /.svn$
+ /.git$
+
+
+
+
+
diff --git a/library/protocol/CONNEG/library/fitness_and_quality.e b/library/protocol/CONNEG/library/fitness_and_quality.e
new file mode 100644
index 00000000..a851ecde
--- /dev/null
+++ b/library/protocol/CONNEG/library/fitness_and_quality.e
@@ -0,0 +1,81 @@
+note
+ description: "Summary description for {FITNESS_AND_QUALITY}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ FITNESS_AND_QUALITY
+
+inherit
+ COMPARABLE
+
+ DEBUG_OUTPUT
+ undefine
+ is_equal
+ end
+
+create
+ make
+
+feature -- Initialization
+
+ make (a_fitness: INTEGER; a_quality: REAL_64)
+ do
+ fitness := a_fitness
+ quality := a_quality
+ create mime_type.make_empty
+ ensure
+ fitness_assigned : fitness = a_fitness
+ quality_assigned : quality = a_quality
+ end
+
+feature -- Access
+
+ fitness: INTEGER
+
+ quality: REAL_64
+
+ mime_type: STRING
+ -- optionally used
+ -- empty by default
+
+
+feature -- Status report
+
+ debug_output: STRING
+ -- String that should be displayed in debugger to represent `Current'.
+ do
+ create Result.make_from_string (mime_type)
+ Result.append (" (")
+ Result.append ("quality=" + quality.out)
+ Result.append (" ; fitness=" + fitness.out)
+ Result.append (" )")
+ end
+
+feature -- Element Change
+
+ set_mime_type (a_mime_type: STRING)
+ -- set mime_type with `a_mime_type'
+ do
+ mime_type := a_mime_type
+ ensure
+ mime_type_assigned : mime_type.same_string (a_mime_type)
+ end
+
+feature -- Comparision
+
+ is_less alias "<" (other: like Current): BOOLEAN
+ -- Is current object less than `other'?
+ do
+ if fitness = other.fitness then
+ Result := quality < other.quality
+ else
+ Result := fitness < other.fitness
+ end
+ end
+note
+ copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+end
+
diff --git a/library/protocol/CONNEG/library/language_parse.e b/library/protocol/CONNEG/library/language_parse.e
new file mode 100644
index 00000000..4373a65a
--- /dev/null
+++ b/library/protocol/CONNEG/library/language_parse.e
@@ -0,0 +1,352 @@
+note
+ description: "Summary description for {LANGUAGE_PARSE}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+ description : "Language Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4"
+
+class
+ LANGUAGE_PARSE
+inherit
+ REFACTORING_HELPER
+
+feature -- Parser
+
+ parse_mime_type (a_mime_type: STRING): LANGUAGE_RESULTS
+ -- 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'})
+ local
+ l_parts: LIST [STRING]
+ p: STRING
+ sub_parts: LIST [STRING]
+ i: INTEGER
+ l_full_type: STRING
+ l_types: LIST [STRING]
+ do
+ fixme ("Improve code!!!")
+ create Result.make
+ l_parts := a_mime_type.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
+ --Java URLConnection class sends an Accept header that includes a
+ --single "*" - Turn it into a legal wildcard.
+
+ 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_media_range (a_range: STRING): LANGUAGE_RESULTS
+ -- 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'})
+ -- 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_mime_type (a_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_mime_type: STRING; parsed_ranges: LIST [LANGUAGE_RESULTS]): 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. 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(), 'parsed_ranges' must be a list of parsed media ranges.
+ local
+ best_fitness: INTEGER
+ target_q: REAL_64
+ best_fit_q: REAL_64
+ target: LANGUAGE_RESULTS
+ range: LANGUAGE_RESULTS
+ keys: LIST [STRING]
+ param_matches: INTEGER
+ element: detachable STRING
+ l_fitness: INTEGER
+ do
+ best_fitness := -1
+ best_fit_q := 0.0
+ target := parse_media_range (a_mime_type)
+ 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
+ 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 ("*"))
+ )
+ 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
+ parsed_ranges.forth
+ end
+ end
+ create Result.make (best_fitness, best_fit_q)
+ end
+
+ quality_parsed (a_mime_type: STRING; parsed_ranges: LIST [LANGUAGE_RESULTS]): REAL_64
+ -- Find the best match for a given mime-type against a list of ranges that
+ -- have already been parsed by parseMediaRange(). 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: STRING; ranges: STRING): REAL_64
+ -- Returns the quality 'q' of a mime-type when compared against the
+ -- mediaRanges in ranges.
+ local
+ l_ranges : LIST [STRING]
+ res : ARRAYED_LIST [LANGUAGE_RESULTS]
+ p_res : LANGUAGE_RESULTS
+ do
+ l_ranges := ranges.split (',')
+ from
+ create res.make (10);
+ l_ranges.start
+ until
+ l_ranges.after
+ loop
+ p_res := parse_media_range (l_ranges.item_for_iteration)
+ res.put_left (p_res)
+ l_ranges.forth
+ end
+ Result := quality_parsed (a_mime_type, res)
+ end
+
+ best_match (supported: LIST [STRING]; header: STRING): STRING
+ -- Choose the mime-type 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 [STRING]
+ p_res: LANGUAGE_RESULTS
+ fitness_and_quality, first_one: detachable FITNESS_AND_QUALITY
+ s: STRING
+ do
+ l_res := 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_media_range (l_res.item_for_iteration)
+ l_header_results.force (p_res)
+ l_res.forth
+ end
+
+ create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (supported.count)
+
+ from
+ supported.start
+ until
+ supported.after
+ loop
+ fitness_and_quality := fitness_and_quality_parsed (supported.item_for_iteration, l_header_results)
+ fitness_and_quality.set_mime_type (mime_type (supported.item_for_iteration))
+ weighted_matches.force (fitness_and_quality)
+ supported.forth
+ end
+
+ --| Keep only top quality+fitness types
+ from
+ weighted_matches.start
+ first_one := weighted_matches.item
+ weighted_matches.forth
+ until
+ weighted_matches.after
+ loop
+ fitness_and_quality := weighted_matches.item
+ if first_one < fitness_and_quality then
+ first_one := fitness_and_quality
+ if not weighted_matches.isfirst then
+ from
+ weighted_matches.back
+ until
+ weighted_matches.before
+ loop
+ weighted_matches.remove
+ weighted_matches.back
+ end
+ weighted_matches.forth
+ end
+ check weighted_matches.item = fitness_and_quality end
+ weighted_matches.forth
+ elseif first_one.is_equal (fitness_and_quality) then
+ weighted_matches.forth
+ else
+ check first_one > fitness_and_quality end
+ weighted_matches.remove
+ end
+ end
+ if first_one /= Void and then first_one.quality /= 0.0 then
+ if weighted_matches.count = 1 then
+ Result := first_one.mime_type
+ else
+ from
+ fitness_and_quality := Void
+ l_header_results.start
+ until
+ l_header_results.after or fitness_and_quality /= Void
+ loop
+ s := l_header_results.item.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
+
+feature {NONE} -- Implementation
+
+ mime_type (s: STRING): STRING
+ local
+ p: INTEGER
+ do
+ p := s.index_of (';', 1)
+ if p > 0 then
+ Result := trim (s.substring (1, p - 1))
+ else
+ Result := trim (s.string)
+ end
+ end
+
+ trim (a_string: STRING): STRING
+ -- trim whitespace from the beginning and end of a string
+ require
+ valid_argument : a_string /= Void
+ do
+ a_string.left_adjust
+ a_string.right_justify
+ Result := a_string
+ ensure
+ result_same_as_argument: a_string = Result
+ end
+
+note
+ copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+end
diff --git a/library/protocol/CONNEG/library/language_results.e b/library/protocol/CONNEG/library/language_results.e
new file mode 100644
index 00000000..fe8d0aa0
--- /dev/null
+++ b/library/protocol/CONNEG/library/language_results.e
@@ -0,0 +1,143 @@
+note
+ description: "Summary description for {LANGUAGE_RESULTS}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ LANGUAGE_RESULTS
+inherit
+ ANY
+ redefine
+ out
+ end
+
+ DEBUG_OUTPUT
+ redefine
+ out
+ end
+
+create
+ make
+
+feature -- Initialization
+
+ make
+ 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
+
+ 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
+
+ out: STRING
+ -- Representation of the current object
+ do
+ create Result.make_from_string ("(")
+ if attached type as t then
+ Result.append_string ("'" + t + "',")
+ end
+ if attached sub_type as st then
+ Result.append_string (" '" + st + "',")
+ end
+ Result.append_string (" {")
+
+ from
+ params.start
+ until
+ params.after
+ loop
+ Result.append ("'" + params.key_for_iteration + "':'" + params.item_for_iteration + "',");
+ params.forth
+ end
+ Result.append ("})")
+ end
+
+ debug_output: STRING
+ -- String that should be displayed in debugger to represent `Current'.
+ do
+ Result := out
+ end
+
+feature {NONE} -- Implementation
+
+ params: HASH_TABLE [STRING, STRING]
+ --dictionary of all the parameters for the media range
+
+;note
+ copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+end
diff --git a/library/protocol/CONNEG/library/license.lic b/library/protocol/CONNEG/library/license.lic
new file mode 100644
index 00000000..42cd9b4e
--- /dev/null
+++ b/library/protocol/CONNEG/library/license.lic
@@ -0,0 +1,4 @@
+${NOTE_KEYWORD}
+ copyright: "2011-${YEAR}, Javier Velilla, Jocelyn Fiat and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+
diff --git a/library/protocol/CONNEG/library/mime_parse.e b/library/protocol/CONNEG/library/mime_parse.e
new file mode 100644
index 00000000..9d92ba36
--- /dev/null
+++ b/library/protocol/CONNEG/library/mime_parse.e
@@ -0,0 +1,349 @@
+note
+ description: "Summary description for {MIME_PARSE}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+ description : "Accept Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1"
+class
+ MIME_PARSE
+
+inherit
+ REFACTORING_HELPER
+
+feature -- Parser
+
+ parse_mime_type (a_mime_type: STRING): PARSE_RESULTS
+ -- 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'})
+ local
+ l_parts: LIST [STRING]
+ p: STRING
+ sub_parts: LIST [STRING]
+ i: INTEGER
+ l_full_type: STRING
+ l_types: LIST [STRING]
+ do
+ fixme ("Improve code!!!")
+ create Result.make
+ l_parts := a_mime_type.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
+ --Java URLConnection class sends an Accept header that includes a
+ --single "*" - Turn it into a legal wildcard.
+
+ l_full_type := trim (l_parts[1])
+ if l_full_type.same_string ("*") then
+ l_full_type := "*/*"
+ end
+ l_types := l_full_type.split ('/')
+ Result.set_type (trim (l_types[1]))
+ Result.set_sub_type (trim (l_types[2]))
+ end
+
+ parse_media_range (a_range: STRING): PARSE_RESULTS
+ -- 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'})
+ -- 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_mime_type (a_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_mime_type: STRING; parsed_ranges: LIST [PARSE_RESULTS]): 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. 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(), 'parsed_ranges' must be a list of parsed media ranges.
+ local
+ best_fitness: INTEGER
+ target_q: REAL_64
+ best_fit_q: REAL_64
+ target: PARSE_RESULTS
+ range: PARSE_RESULTS
+ keys: LIST [STRING]
+ param_matches: INTEGER
+ element: detachable STRING
+ l_fitness: INTEGER
+ do
+ best_fitness := -1
+ best_fit_q := 0.0
+ target := parse_media_range (a_mime_type)
+ 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 and
+ attached target.sub_type 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.sub_type 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
+ 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 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.item ("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: STRING; parsed_ranges: LIST [PARSE_RESULTS]): REAL_64
+ -- Find the best match for a given mime-type against a list of ranges that
+ -- have already been parsed by parseMediaRange(). 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: STRING; ranges: STRING): REAL_64
+ -- Returns the quality 'q' of a mime-type when compared against the
+ -- mediaRanges in ranges.
+ local
+ l_ranges : LIST [STRING]
+ res : ARRAYED_LIST [PARSE_RESULTS]
+ p_res : PARSE_RESULTS
+ do
+ l_ranges := ranges.split (',')
+ from
+ create res.make (10);
+ l_ranges.start
+ until
+ l_ranges.after
+ loop
+ p_res := parse_media_range (l_ranges.item_for_iteration)
+ res.put_left (p_res)
+ l_ranges.forth
+ end
+ Result := quality_parsed (a_mime_type, res)
+ end
+
+ best_match (supported: LIST [STRING]; header: STRING): STRING
+ -- Choose the mime-type with the highest fitness score and quality ('q') from a list of candidates.
+ local
+ l_header_results: LIST [PARSE_RESULTS]
+ weighted_matches: LIST [FITNESS_AND_QUALITY]
+ l_res: LIST [STRING]
+ p_res: PARSE_RESULTS
+ fitness_and_quality, first_one: detachable FITNESS_AND_QUALITY
+ s: STRING
+ do
+ l_res := header.split (',')
+ create {ARRAYED_LIST [PARSE_RESULTS]} l_header_results.make (l_res.count)
+
+ fixme("Extract method!!!")
+ from
+ l_res.start
+ until
+ l_res.after
+ loop
+ p_res := parse_media_range (l_res.item_for_iteration)
+ l_header_results.force (p_res)
+ l_res.forth
+ end
+
+ create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (supported.count)
+
+ from
+ supported.start
+ until
+ supported.after
+ loop
+ fitness_and_quality := fitness_and_quality_parsed (supported.item_for_iteration, l_header_results)
+ fitness_and_quality.set_mime_type (mime_type (supported.item_for_iteration))
+ weighted_matches.force (fitness_and_quality)
+ supported.forth
+ end
+
+ --| Keep only top quality+fitness types
+ from
+ weighted_matches.start
+ first_one := weighted_matches.item
+ weighted_matches.forth
+ until
+ weighted_matches.after
+ loop
+ fitness_and_quality := weighted_matches.item
+ if first_one < fitness_and_quality then
+ first_one := fitness_and_quality
+ if not weighted_matches.isfirst then
+ from
+ weighted_matches.back
+ until
+ weighted_matches.before
+ loop
+ weighted_matches.remove
+ weighted_matches.back
+ end
+ weighted_matches.forth
+ end
+ check weighted_matches.item = fitness_and_quality end
+ weighted_matches.forth
+ elseif first_one.is_equal (fitness_and_quality) then
+ weighted_matches.forth
+ else
+ check first_one > fitness_and_quality end
+ weighted_matches.remove
+ end
+ end
+ if first_one /= Void and then first_one.quality /= 0.0 then
+ if weighted_matches.count = 1 then
+ Result := first_one.mime_type
+ else
+ from
+ fitness_and_quality := Void
+ l_header_results.start
+ until
+ l_header_results.after or fitness_and_quality /= Void
+ loop
+ s := l_header_results.item.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
+
+feature {NONE} -- Implementation
+
+ mime_type (s: STRING): STRING
+ local
+ p: INTEGER
+ do
+ p := s.index_of (';', 1)
+ if p > 0 then
+ Result := trim (s.substring (1, p - 1))
+ else
+ Result := trim (s.string)
+ end
+ end
+
+ trim (a_string: STRING): STRING
+ -- trim whitespace from the beginning and end of a string
+ require
+ valid_argument : a_string /= Void
+ do
+ a_string.left_adjust
+ a_string.right_justify
+ Result := a_string
+ ensure
+ result_same_as_argument: a_string = Result
+ end
+
+note
+ copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+end
diff --git a/library/protocol/CONNEG/library/parse_results.e b/library/protocol/CONNEG/library/parse_results.e
new file mode 100644
index 00000000..185995ea
--- /dev/null
+++ b/library/protocol/CONNEG/library/parse_results.e
@@ -0,0 +1,144 @@
+note
+ description: "Summary description for {PARSE_RESULTS}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ PARSE_RESULTS
+
+inherit
+ ANY
+ redefine
+ out
+ end
+
+ DEBUG_OUTPUT
+ redefine
+ out
+ end
+
+create
+ make
+
+feature -- Initialization
+
+ make
+ 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
+
+ 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 := "*/" + a_sub_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
+
+ out: STRING
+ -- Representation of the current object
+ do
+ create Result.make_from_string ("(")
+ if attached type as t then
+ Result.append_string ("'" + t + "',")
+ end
+ if attached sub_type as st then
+ Result.append_string (" '" + st + "',")
+ end
+ Result.append_string (" {")
+
+ from
+ params.start
+ until
+ params.after
+ loop
+ Result.append ("'" + params.key_for_iteration + "':'" + params.item_for_iteration + "',");
+ params.forth
+ end
+ Result.append ("})")
+ end
+
+ debug_output: STRING
+ -- String that should be displayed in debugger to represent `Current'.
+ do
+ Result := out
+ end
+
+feature {NONE} -- Implementation
+
+ params: HASH_TABLE [STRING, STRING]
+ --dictionary of all the parameters for the media range
+
+;note
+ copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+end
diff --git a/library/protocol/CONNEG/library/shared_conneg.e b/library/protocol/CONNEG/library/shared_conneg.e
new file mode 100644
index 00000000..ffa6a47f
--- /dev/null
+++ b/library/protocol/CONNEG/library/shared_conneg.e
@@ -0,0 +1,30 @@
+note
+ description: "Summary description for {SHARED_MIME}."
+ 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-2011, Javier Velilla, Jocelyn Fiat and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+end
diff --git a/library/protocol/CONNEG/library/variants.e b/library/protocol/CONNEG/library/variants.e
new file mode 100644
index 00000000..3379edc9
--- /dev/null
+++ b/library/protocol/CONNEG/library/variants.e
@@ -0,0 +1,76 @@
+note
+ description: "Summary description for {VARIANTS}. 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
+ VARIANTS
+
+inherit
+ SHARED_CONNEG
+ REFACTORING_HELPER
+feature -- Media Type Negotiation
+
+ media_type_preference ( mime_types_supported : LIST[STRING]; header : STRING) : STRING
+ -- 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
+ do
+ Result := mime.best_match (mime_types_supported, header)
+ end
+
+
+feature -- Encoding Negotiation
+
+ charset_preference (server_charset_supported : LIST[STRING]; header: STRING) : STRING
+ -- 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
+ do
+ Result := common.best_match (server_charset_supported, header)
+ end
+
+
+feature -- Compression Negotiation
+
+ encoding_preference (server_encoding_supported : LIST[STRING]; header: STRING) : STRING
+ -- 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
+ do
+ Result := common.best_match (server_encoding_supported, header)
+ end
+
+feature -- Language Negotiation
+
+ language_preference (server_language_supported : LIST[STRING]; header: STRING) : STRING
+ -- 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
+ do
+ Result := language.best_match (server_language_supported, header)
+ end
+
+note
+ copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+end
+
+
diff --git a/library/protocol/CONNEG/run_test.rb b/library/protocol/CONNEG/run_test.rb
new file mode 100644
index 00000000..4ee95c80
--- /dev/null
+++ b/library/protocol/CONNEG/run_test.rb
@@ -0,0 +1,79 @@
+#!/usr/bin/env ruby
+# Niklaus Giger, 15.01.2011
+# Small ruby-script run all tests using ec (the Eiffel compiler)
+# we assumen that ec outputs everything in english!
+
+# For the command line options look at
+# http://docs.eiffel.com/book/eiffelstudio/eiffelstudio-command-line-options
+# we use often the -batch open.
+#
+# TODO: Fix problems when compiling takes too long and/or there
+# are ec process lingering around from a previous failed build
+
+require 'tempfile'
+require 'fileutils'
+
+# Override system command.
+# run command. if not successful, complain and exit with error
+def system(cmd)
+ puts cmd
+ res = Kernel.system(cmd)
+ if !res
+ puts "Failed running: #{cmd}"
+ exit 2
+ end
+end
+
+
+def runTestForProject(where)
+ if !File.directory?(where)
+ puts "Directory #{where} does not exist"
+ exit 2
+ end
+
+ # create a temporary file with input for the
+ # interactive mode of ec
+ commands2run=<
+
+
+
+
+
+
+
+
+
+ /EIFGENs$
+ /CVS$
+ /.svn$
+ /.git$
+
+
+
+
diff --git a/library/protocol/CONNEG/test/test.ecf b/library/protocol/CONNEG/test/test.ecf
new file mode 100644
index 00000000..dbacffdd
--- /dev/null
+++ b/library/protocol/CONNEG/test/test.ecf
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+ /.git$
+ /EIFGENs$
+ /CVS$
+ /.svn$
+
+
+
+