Change structure of EWF, to follow better categorization
This commit is contained in:
1
library/network/protocol/CONNEG/.gitignore
vendored
Normal file
1
library/network/protocol/CONNEG/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
EIFGENs/
|
||||
125
library/network/protocol/CONNEG/README.md
Normal file
125
library/network/protocol/CONNEG/README.md
Normal file
@@ -0,0 +1,125 @@
|
||||
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
|
||||
|
||||
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,
|
||||
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
|
||||
|
||||
- How to implement Language Negotiation?
|
||||
Hint: Use CONNEG_SERVER_SIDE.language_preference
|
||||
|
||||
- How to implement Character encoding Negotiation?
|
||||
Hint: Use CONNEG_SERVER_SIDE.charset_preference
|
||||
|
||||
- How to implement Compression Negotiation?
|
||||
Hint: Use CONNEG_SERVER_SIDE.encoding_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
|
||||
|
||||
17
library/network/protocol/CONNEG/conneg-safe.ecf
Normal file
17
library/network/protocol/CONNEG/conneg-safe.ecf
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-7-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-7-0 http://www.eiffel.com/developers/xml/configuration-1-7-0.xsd" name="conneg" uuid="D15DC4C4-D74F-4E4B-B4B1-2B03A35A284D" library_target="conneg">
|
||||
<target name="conneg">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<cluster name="conneg" location=".\src" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
17
library/network/protocol/CONNEG/conneg.ecf
Normal file
17
library/network/protocol/CONNEG/conneg.ecf
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-7-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-7-0 http://www.eiffel.com/developers/xml/configuration-1-7-0.xsd" name="conneg" uuid="D15DC4C4-D74F-4E4B-B4B1-2B03A35A284D" library_target="conneg">
|
||||
<target name="conneg">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
<exclude>/.git$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<cluster name="conneg" location=".\src" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
4
library/network/protocol/CONNEG/license.lic
Normal file
4
library/network/protocol/CONNEG/license.lic
Normal file
@@ -0,0 +1,4 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
79
library/network/protocol/CONNEG/run_test.rb
Normal file
79
library/network/protocol/CONNEG/run_test.rb
Normal file
@@ -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=<<EOF
|
||||
T
|
||||
E
|
||||
q
|
||||
EOF
|
||||
file = Tempfile.new('commands2run')
|
||||
file.puts commands2run
|
||||
file.close
|
||||
|
||||
Dir.chdir(where)
|
||||
# First we have to remove old compilation
|
||||
FileUtils.rm_rf("EIFGENs")
|
||||
|
||||
# compile the library
|
||||
cmd = "ec -config library/emime-safe.ecf -target emime -batch -c_compile"
|
||||
res = system(cmd)
|
||||
|
||||
# compile the test
|
||||
cmd = "ec -config test/test-safe.ecf -target test -batch -c_compile"
|
||||
res = system(cmd)
|
||||
|
||||
|
||||
logFile = "#{__FILE__}.log"
|
||||
sleep 1
|
||||
cmd = "ec -config test/test-safe.ecf -target test -batch -loop 1>#{logFile} 2>#{__FILE__}.auto_test_output <#{file.path}"
|
||||
res = system(cmd)
|
||||
m= nil
|
||||
IO.readlines(logFile).each{
|
||||
|line|
|
||||
m = /(\d+) tests total \((\d+) executed, (\d+) failing, (\d+) unresolved/.match(line)
|
||||
break if m
|
||||
}
|
||||
|
||||
puts
|
||||
if m[3].to_i == 0 and m[4].to_i == 0 then
|
||||
puts "#{m[1]} tests completed successfully"
|
||||
else
|
||||
puts "Failures while running #{m[1]} failed. #{m[2]} executed #{m[3]} failures #{m[4]} unresolved"
|
||||
exit 2
|
||||
end
|
||||
end
|
||||
|
||||
runTestForProject(Dir.pwd)
|
||||
|
||||
4
library/network/protocol/CONNEG/src/.gitignore
vendored
Normal file
4
library/network/protocol/CONNEG/src/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*~
|
||||
EIFGEN* # ignore all files in the EIFGENs/ directory
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
note
|
||||
description: "Summary description for {CHARACTER_ENCODING_VARIANT_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CHARACTER_ENCODING_VARIANT_RESULTS
|
||||
feature
|
||||
character_type : detachable STRING
|
||||
set_character_type ( a_character_type: STRING)
|
||||
do
|
||||
character_type := a_character_type
|
||||
ensure
|
||||
set_character_type : a_character_type ~ character_type
|
||||
end
|
||||
|
||||
variant_header : detachable STRING
|
||||
set_variant_header
|
||||
do
|
||||
variant_header := "Accept-Charset"
|
||||
end
|
||||
|
||||
supported_variants : detachable LIST[STRING]
|
||||
set_supported_variants (a_supported : LIST[STRING])
|
||||
do
|
||||
supported_variants := a_supported
|
||||
ensure
|
||||
set_supported_variants : supported_variants = a_supported
|
||||
end
|
||||
|
||||
is_acceptable : BOOLEAN
|
||||
|
||||
set_acceptable ( acceptable : BOOLEAN)
|
||||
do
|
||||
is_acceptable := acceptable
|
||||
ensure
|
||||
is_acceptable = acceptable
|
||||
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
|
||||
@@ -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
|
||||
119
library/network/protocol/CONNEG/src/common_results.e
Normal file
119
library/network/protocol/CONNEG/src/common_results.e
Normal file
@@ -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
|
||||
@@ -0,0 +1,44 @@
|
||||
note
|
||||
description: "Summary description for {COMPRESSION_VARIANT_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
COMPRESSION_VARIANT_RESULTS
|
||||
|
||||
feature
|
||||
compression_type : detachable STRING
|
||||
set_compression_type ( a_compression_type: STRING)
|
||||
do
|
||||
compression_type := a_compression_type
|
||||
ensure
|
||||
set_compression_type : a_compression_type ~ compression_type
|
||||
end
|
||||
|
||||
variant_header : detachable STRING
|
||||
set_variant_header
|
||||
do
|
||||
variant_header := "Accept-Encoding"
|
||||
end
|
||||
|
||||
supported_variants : detachable LIST[STRING]
|
||||
set_supported_variants (a_supported : LIST[STRING])
|
||||
do
|
||||
supported_variants := a_supported
|
||||
ensure
|
||||
set_supported_variants : supported_variants = a_supported
|
||||
end
|
||||
|
||||
is_acceptable : BOOLEAN
|
||||
|
||||
set_acceptable ( acceptable : BOOLEAN)
|
||||
do
|
||||
is_acceptable := acceptable
|
||||
ensure
|
||||
is_acceptable = acceptable
|
||||
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
|
||||
216
library/network/protocol/CONNEG/src/conneg_server_side.e
Normal file
216
library/network/protocol/CONNEG/src/conneg_server_side.e
Normal file
@@ -0,0 +1,216 @@
|
||||
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
|
||||
CONNEG_SERVER_SIDE
|
||||
|
||||
inherit
|
||||
SHARED_CONNEG
|
||||
REFACTORING_HELPER
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
make ( a_mime: STRING; a_language : STRING; a_charset :STRING; an_encoding: STRING)
|
||||
do
|
||||
set_mime_default (a_mime)
|
||||
set_language_default (a_language)
|
||||
set_charset_default (a_charset)
|
||||
set_encoding_defautl (an_encoding)
|
||||
end
|
||||
feature -- Server Side Defaults Formats
|
||||
mime_default : STRING
|
||||
|
||||
set_mime_default ( a_mime: STRING)
|
||||
-- set the mime_default with `a_mime'
|
||||
do
|
||||
mime_default := a_mime
|
||||
ensure
|
||||
set_mime_default: a_mime ~ mime_default
|
||||
end
|
||||
|
||||
|
||||
language_default : STRING
|
||||
|
||||
set_language_default (a_language : STRING)
|
||||
-- set the language_default with `a_language'
|
||||
do
|
||||
language_default := a_language
|
||||
ensure
|
||||
set_language : a_language ~ language_default
|
||||
end
|
||||
|
||||
|
||||
charset_default : STRING
|
||||
|
||||
set_charset_default (a_charset : STRING)
|
||||
-- set the charset_default with `a_charset'
|
||||
do
|
||||
charset_default := a_charset
|
||||
ensure
|
||||
set_charset : a_charset ~ charset_default
|
||||
end
|
||||
|
||||
|
||||
encoding_default : STRING
|
||||
|
||||
set_encoding_defautl (an_encoding : STRING)
|
||||
do
|
||||
encoding_default := an_encoding
|
||||
ensure
|
||||
set_encoding : an_encoding ~ encoding_default
|
||||
end
|
||||
|
||||
|
||||
|
||||
feature -- Media Type Negotiation
|
||||
|
||||
media_type_preference ( mime_types_supported : LIST[STRING]; header : STRING) : 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
|
||||
local
|
||||
mime_match: STRING
|
||||
do
|
||||
create Result
|
||||
if 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_media_type (mime_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
mime_match := mime.best_match (mime_types_supported, header)
|
||||
if mime_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 (mime_types_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_media_type(mime_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
feature -- Encoding Negotiation
|
||||
|
||||
charset_preference (server_charset_supported : LIST[STRING]; header: STRING) : 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
|
||||
local
|
||||
charset_match : STRING
|
||||
do
|
||||
create Result
|
||||
if header.is_empty then
|
||||
-- the request has no Accept-Charset header, ie the header is empty, in this case use default charset encoding
|
||||
-- (UTF-8)
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_character_type (charset_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
charset_match := common.best_match (server_charset_supported, header)
|
||||
if 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 (server_charset_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_character_type(charset_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Compression Negotiation
|
||||
|
||||
encoding_preference (server_encoding_supported : LIST[STRING]; header: STRING) : 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
|
||||
local
|
||||
compression_match : STRING
|
||||
do
|
||||
create Result
|
||||
if 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_compression_type (encoding_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
compression_match := common.best_match (server_encoding_supported, header)
|
||||
if 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 (server_encoding_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_compression_type(compression_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
feature -- Language Negotiation
|
||||
|
||||
language_preference (server_language_supported : LIST[STRING]; header: STRING) : 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
|
||||
local
|
||||
language_match: STRING
|
||||
do
|
||||
create Result
|
||||
if 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_language_type (language_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
language_match := language.best_match (server_language_supported, header)
|
||||
if 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 (server_language_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_language_type(language_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Apache Conneg Algorithm
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
|
||||
81
library/network/protocol/CONNEG/src/fitness_and_quality.e
Normal file
81
library/network/protocol/CONNEG/src/fitness_and_quality.e
Normal file
@@ -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
|
||||
|
||||
352
library/network/protocol/CONNEG/src/language_parse.e
Normal file
352
library/network/protocol/CONNEG/src/language_parse.e
Normal file
@@ -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
|
||||
143
library/network/protocol/CONNEG/src/language_results.e
Normal file
143
library/network/protocol/CONNEG/src/language_results.e
Normal file
@@ -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
|
||||
@@ -0,0 +1,46 @@
|
||||
note
|
||||
description: "Summary description for {LANGUAGE_VARIANT_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
LANGUAGE_VARIANT_RESULTS
|
||||
|
||||
feature
|
||||
language_type : detachable STRING
|
||||
set_language_type ( a_language_type: STRING)
|
||||
do
|
||||
language_type := a_language_type
|
||||
ensure
|
||||
set_language_type : a_language_type ~ language_type
|
||||
end
|
||||
|
||||
variant_header : detachable STRING
|
||||
set_variant_header
|
||||
do
|
||||
variant_header := "Accept-Language"
|
||||
end
|
||||
|
||||
supported_variants : detachable LIST[STRING]
|
||||
set_supported_variants (a_supported : LIST[STRING])
|
||||
do
|
||||
supported_variants := a_supported
|
||||
ensure
|
||||
set_supported_variants : supported_variants = a_supported
|
||||
end
|
||||
|
||||
is_acceptable : BOOLEAN
|
||||
|
||||
set_acceptable ( acceptable : BOOLEAN)
|
||||
do
|
||||
is_acceptable := acceptable
|
||||
ensure
|
||||
is_acceptable = acceptable
|
||||
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
|
||||
@@ -0,0 +1,47 @@
|
||||
note
|
||||
description: "Summary description for {MEDIA_TYPE_VARIANT_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
MEDIA_TYPE_VARIANT_RESULTS
|
||||
|
||||
feature
|
||||
|
||||
media_type : detachable STRING
|
||||
set_media_type ( a_media_type: STRING)
|
||||
do
|
||||
media_type := a_media_type
|
||||
ensure
|
||||
set_media_type : a_media_type ~ media_type
|
||||
end
|
||||
|
||||
variant_header : detachable STRING
|
||||
set_variant_header
|
||||
do
|
||||
variant_header := "Accept"
|
||||
end
|
||||
|
||||
supported_variants : detachable LIST[STRING]
|
||||
set_supported_variants (a_supported : LIST[STRING])
|
||||
do
|
||||
supported_variants := a_supported
|
||||
ensure
|
||||
set_supported_variants : supported_variants = a_supported
|
||||
end
|
||||
|
||||
is_acceptable : BOOLEAN
|
||||
|
||||
set_acceptable ( acceptable : BOOLEAN)
|
||||
do
|
||||
is_acceptable := acceptable
|
||||
ensure
|
||||
is_acceptable = acceptable
|
||||
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
|
||||
349
library/network/protocol/CONNEG/src/mime_parse.e
Normal file
349
library/network/protocol/CONNEG/src/mime_parse.e
Normal file
@@ -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
|
||||
144
library/network/protocol/CONNEG/src/parse_results.e
Normal file
144
library/network/protocol/CONNEG/src/parse_results.e
Normal file
@@ -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
|
||||
30
library/network/protocol/CONNEG/src/shared_conneg.e
Normal file
30
library/network/protocol/CONNEG/src/shared_conneg.e
Normal file
@@ -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
|
||||
58
library/network/protocol/CONNEG/src/variant_results.e
Normal file
58
library/network/protocol/CONNEG/src/variant_results.e
Normal file
@@ -0,0 +1,58 @@
|
||||
note
|
||||
description: "Summary description for {VARIANT_RESULTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
VARIANT_RESULTS
|
||||
|
||||
feature -- Mime, Language, Charset and Encoding Results
|
||||
|
||||
mime_result : detachable STRING
|
||||
|
||||
set_mime_result ( a_mime: STRING)
|
||||
-- set the mime_result with `a_mime'
|
||||
do
|
||||
mime_result := a_mime
|
||||
ensure
|
||||
set_mime_result: a_mime ~ mime_result
|
||||
end
|
||||
|
||||
|
||||
language_result : detachable STRING
|
||||
|
||||
set_language_result (a_language : STRING)
|
||||
-- set the language_result with `a_language'
|
||||
do
|
||||
language_result := a_language
|
||||
ensure
|
||||
set_language : a_language ~ language_result
|
||||
end
|
||||
|
||||
|
||||
charset_result : detachable STRING
|
||||
|
||||
set_charset_result (a_charset : STRING)
|
||||
-- set the charset_result with `a_charset'
|
||||
do
|
||||
charset_result := a_charset
|
||||
ensure
|
||||
set_charset : a_charset ~ charset_result
|
||||
end
|
||||
|
||||
|
||||
encoding_result : detachable STRING
|
||||
|
||||
set_encoding_defautl (an_encoding : STRING)
|
||||
do
|
||||
encoding_result := an_encoding
|
||||
ensure
|
||||
set_encoding : an_encoding ~ encoding_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
|
||||
4
library/network/protocol/CONNEG/test/.gitignore
vendored
Normal file
4
library/network/protocol/CONNEG/test/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*~
|
||||
EIFGEN* # ignore all files in the EIFGENs/ directory
|
||||
|
||||
|
||||
87
library/network/protocol/CONNEG/test/application.e
Normal file
87
library/network/protocol/CONNEG/test/application.e
Normal file
@@ -0,0 +1,87 @@
|
||||
note
|
||||
description : "eMIME application root class"
|
||||
date : "$Date$"
|
||||
revision : "$Revision$"
|
||||
|
||||
class
|
||||
APPLICATION
|
||||
|
||||
inherit
|
||||
ARGUMENTS
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
local
|
||||
mime_parse : MIME_PARSE
|
||||
accept : STRING
|
||||
charset_parse : COMMON_ACCEPT_HEADER_PARSER
|
||||
language : LANGUAGE_PARSE
|
||||
do
|
||||
create mime_parse
|
||||
-- parse_result := mime_parse.parse_mime_type ("application/xhtml;q=0.5")
|
||||
-- print ("%N"+parse_result.out)
|
||||
|
||||
-- parse_result := mime_parse.parse_media_range ("application/xml;q=1")
|
||||
-- print ("%N"+parse_result.out)
|
||||
-- check
|
||||
-- "('application', 'xml', {'q':'1',})" ~ mime_parse.parse_media_range ("application/xml;q=1").out
|
||||
-- end
|
||||
|
||||
-- parse_result := mime_parse.parse_media_range ("application/xml")
|
||||
-- print ("%N"+parse_result.out)
|
||||
-- check
|
||||
-- "('application', 'xml', {'q':'1',})" ~ mime_parse.parse_media_range ("application/xml;q=1").out
|
||||
-- end
|
||||
-- assertEquals("('application', 'xml', {'q':'1',})", MIMEParse
|
||||
-- .parseMediaRange("application/xml").toString());
|
||||
-- assertEquals("('application', 'xml', {'q':'1',})", MIMEParse
|
||||
-- .parseMediaRange("application/xml;q=").toString());
|
||||
-- assertEquals("('application', 'xml', {'q':'1',})", MIMEParse
|
||||
-- .parseMediaRange("application/xml ; q=").toString());
|
||||
-- assertEquals("('application', 'xml', {'b':'other','q':'1',})",
|
||||
-- MIMEParse.parseMediaRange("application/xml ; q=1;b=other")
|
||||
-- .toString());
|
||||
-- assertEquals("('application', 'xml', {'b':'other','q':'1',})",
|
||||
-- MIMEParse.parseMediaRange("application/xml ; q=2;b=other")
|
||||
-- .toString());
|
||||
-- // Java URLConnection class sends an Accept header that includes a
|
||||
-- // single *
|
||||
-- assertEquals("('*', '*', {'q':'.2',})", MIMEParse.parseMediaRange(
|
||||
-- " *; q=.2").toString());
|
||||
|
||||
accept := "application/atom+xml;q=1.0,application/xml;q=0.6,text/html"
|
||||
print ("%N"+mime_parse.quality ("text/html;q=1.0", accept).out)
|
||||
print ("%N"+mime_parse.quality ("application/xml", accept).out)
|
||||
print ("%N"+mime_parse.quality ("*/*;q=0.1", accept).out)
|
||||
|
||||
accept := "application/atom+xml"
|
||||
print ("%N"+mime_parse.parse_mime_type (accept).out)
|
||||
create charset_parse
|
||||
accept := "iso-8859-5"
|
||||
print ("%N" + charset_parse.parse_common (accept).out)
|
||||
accept := "unicode-1-1;q=0.8"
|
||||
print ("%N" + charset_parse.parse_common (accept).out)
|
||||
|
||||
|
||||
accept:= "iso-8859-5, unicode-1-1;q=0.8"
|
||||
print ("%N"+ charset_parse.quality ("iso-8859-5", accept).out)
|
||||
print ("%N"+ charset_parse.quality ("unicode-1-1", accept).out)
|
||||
print ("%N"+ charset_parse.quality ("iso-8859-1", accept).out)
|
||||
|
||||
|
||||
create language
|
||||
accept :="da, en-gb;q=0.8, en;q=0.7"
|
||||
print (language.best_match (accept.split (','), "da"))
|
||||
print (language.best_match (accept.split (','), "en-*"))
|
||||
|
||||
print ("%N"+language.parse_media_range ("da").out)
|
||||
print ("%N"+language.parse_media_range ("en-gb;q=0.8").out)
|
||||
print ("%N"+language.parse_media_range ("en;q=0.7").out)
|
||||
print ("%N"+language.parse_media_range ("en-*").out)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,57 @@
|
||||
note
|
||||
description: "Summary description for {COMMON_ACCEPT_HEADER_PARSER_TEST}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
COMMON_ACCEPT_HEADER_PARSER_TEST
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
redefine
|
||||
on_prepare
|
||||
end
|
||||
|
||||
feature {NONE} -- Events
|
||||
|
||||
on_prepare
|
||||
-- Called after all initializations in `default_create'.
|
||||
do
|
||||
create parser
|
||||
end
|
||||
|
||||
feature -- Test routines
|
||||
|
||||
test_parse_charsets
|
||||
do
|
||||
assert ("Expected ('iso-8859-5', {'q':'1.0',})", parser.parse_common("iso-8859-5").out.same_string("('iso-8859-5', {'q':'1.0',})") )
|
||||
assert ("Expected ('unicode-1-1', {'q':'0.8',})", parser.parse_common("unicode-1-1;q=0.8").out.same_string("('unicode-1-1', {'q':'0.8',})") )
|
||||
assert ("Expected ('*', {'q':'1.0',})", parser.parse_common("*").out.same_string("('*', {'q':'1.0',})") )
|
||||
end
|
||||
|
||||
|
||||
test_quality_example
|
||||
local
|
||||
accept : STRING
|
||||
do
|
||||
accept := "iso-8859-5, unicode-1-1;q=0.8";
|
||||
assert ("Expected 1.0", 1.0 = parser.quality ("iso-8859-5", accept))
|
||||
assert ("Expected 0.8", 0.8 = parser.quality ("unicode-1-1", accept))
|
||||
end
|
||||
|
||||
|
||||
test_best_match
|
||||
local
|
||||
charset_supported : LIST [STRING]
|
||||
l_charsets : STRING
|
||||
do
|
||||
l_charsets := "iso-8859-5, unicode-1-1;q=0.8"
|
||||
charset_supported := l_charsets.split(',')
|
||||
assert ("Expected iso-8859-5", parser.best_match (charset_supported, "*").same_string ("iso-8859-5"))
|
||||
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
|
||||
|
||||
end
|
||||
167
library/network/protocol/CONNEG/test/conneg_server_side_test.e
Normal file
167
library/network/protocol/CONNEG/test/conneg_server_side_test.e
Normal file
@@ -0,0 +1,167 @@
|
||||
note
|
||||
description: "Summary description for {CONNEG_SERVER_SIDE_TEST}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CONNEG_SERVER_SIDE_TEST
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
redefine
|
||||
on_prepare
|
||||
end
|
||||
|
||||
feature {NONE} -- Events
|
||||
|
||||
on_prepare
|
||||
-- Called after all initializations in `default_create'.
|
||||
do
|
||||
create conneg.make ("application/json", "es", "UTF-8", "")
|
||||
-- set default values
|
||||
end
|
||||
|
||||
feature -- Test routines
|
||||
test_media_type_negotiation
|
||||
local
|
||||
media_variants : MEDIA_TYPE_VARIANT_RESULTS
|
||||
mime_types_supported : LIST [STRING]
|
||||
l_types : STRING
|
||||
do
|
||||
-- Scenario 1, the server side does not support client preferences
|
||||
l_types := "application/json,application/xbel+xml,application/xml"
|
||||
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.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.media_type))
|
||||
assert ("Variant header", media_variants.variant_header = Void)
|
||||
|
||||
--Scenario 3, the server select the best match, and set the vary header
|
||||
media_variants := conneg.media_type_preference (mime_types_supported, "text/*,application/json;q=0.5")
|
||||
assert ("Expected Acceptable", media_variants.is_acceptable)
|
||||
assert ("Variants is dettached",media_variants.supported_variants = Void)
|
||||
assert ("Variant Header", media_variants.variant_header.is_equal ("Accept"))
|
||||
assert ("Media Type is application/json", media_variants.media_type.is_equal ("application/json"))
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
test_charset_negotiation
|
||||
local
|
||||
charset_variants : CHARACTER_ENCODING_VARIANT_RESULTS
|
||||
charset_supported : LIST [STRING]
|
||||
l_charset : STRING
|
||||
do
|
||||
-- Scenario 1, the server side does not support client preferences
|
||||
l_charset := "UTF-8, iso-8859-5"
|
||||
charset_supported := l_charset.split(',')
|
||||
charset_variants := conneg.charset_preference (charset_supported, "unicode-1-1")
|
||||
assert ("Expected Not Acceptable", not charset_variants.is_acceptable)
|
||||
assert ("Same Value at 1",charset_supported.at (1).is_equal (charset_variants.supported_variants.at (1)))
|
||||
assert ("Same count",charset_supported.count = charset_variants.supported_variants.count)
|
||||
assert ("Variant header is void",charset_variants.variant_header = Void)
|
||||
assert ("Character type is void",charset_variants.character_type = 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.character_type))
|
||||
assert ("Variant header", charset_variants.variant_header = 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.character_type.is_equal ("iso-8859-5"))
|
||||
end
|
||||
|
||||
test_compression_negotiation
|
||||
local
|
||||
compression_variants : COMPRESSION_VARIANT_RESULTS
|
||||
compression_supported : LIST [STRING]
|
||||
l_compression : STRING
|
||||
do
|
||||
-- Scenario 1, the server side does not support client preferences
|
||||
l_compression := ""
|
||||
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.compression_type = 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.compression_type))
|
||||
assert ("Variant header", compression_variants.variant_header = 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_defautl ("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.compression_type.is_equal ("gzip"))
|
||||
end
|
||||
|
||||
|
||||
|
||||
test_language_negotiation
|
||||
local
|
||||
language_variants : LANGUAGE_VARIANT_RESULTS
|
||||
languages_supported : LIST [STRING]
|
||||
l_languages : STRING
|
||||
do
|
||||
-- Scenario 1, the server side does not support client preferences
|
||||
l_languages := "es,en,en-US,fr;q=0.6"
|
||||
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.language_type = Void)
|
||||
|
||||
|
||||
-- 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.language_type))
|
||||
assert ("Variant header", language_variants.variant_header = 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.language_type.is_equal ("fr"))
|
||||
|
||||
|
||||
end
|
||||
|
||||
feature -- Implementation
|
||||
conneg : CONNEG_SERVER_SIDE
|
||||
end
|
||||
117
library/network/protocol/CONNEG/test/language_parser_test.e
Normal file
117
library/network/protocol/CONNEG/test/language_parser_test.e
Normal file
@@ -0,0 +1,117 @@
|
||||
note
|
||||
description: "Summary description for {LANGUAGE_PARSER_TEST}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
LANGUAGE_PARSER_TEST
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
redefine
|
||||
on_prepare
|
||||
end
|
||||
|
||||
feature {NONE} -- Events
|
||||
|
||||
on_prepare
|
||||
-- Called after all initializations in `default_create'.
|
||||
do
|
||||
create parser
|
||||
end
|
||||
|
||||
feature -- Test routines
|
||||
|
||||
test_parse_media_range
|
||||
do
|
||||
assert ("Expected ('da', {'q':'1.0',})", parser.parse_media_range ("da").out.same_string ("('da', {'q':'1.0',})"));
|
||||
assert ("Expected ('en', 'gb', {'q':'0.8',})", parser.parse_media_range ("en-gb;q=0.8").out.same_string ("('en', 'gb', {'q':'0.8',})"));
|
||||
assert ("Expected ('en', {'q':'0.7',})", parser.parse_media_range ("en;q=0.7").out.same_string ("('en', {'q':'0.7',})"));
|
||||
assert ("Expected ('en', '*', {'q':'1.0',})", parser.parse_media_range ("en-*").out.same_string ("('en', '*', {'q':'1.0',})"));
|
||||
end
|
||||
|
||||
|
||||
test_RFC2616_example
|
||||
local
|
||||
accept : STRING
|
||||
do
|
||||
accept := "da, en-gb;q=0.8, en;q=0.7";
|
||||
assert ("Expected 1.0", 1.0 = parser.quality ("da", accept))
|
||||
assert ("Expected 0.8", 0.8 = parser.quality ("en-gb", accept))
|
||||
assert ("Expected 0.8", 0.8 = parser.quality ("en", accept))
|
||||
assert ("Expected 0.8", 0.8 = parser.quality ("en-*", accept))
|
||||
end
|
||||
|
||||
|
||||
test_best_match
|
||||
local
|
||||
mime_types_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"))
|
||||
|
||||
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 (""))
|
||||
|
||||
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"))
|
||||
|
||||
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"))
|
||||
|
||||
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"))
|
||||
|
||||
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"))
|
||||
|
||||
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"))
|
||||
|
||||
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"))
|
||||
|
||||
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"))
|
||||
|
||||
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"))
|
||||
|
||||
end
|
||||
|
||||
|
||||
test_support_wildcard
|
||||
local
|
||||
mime_types_supported : LIST[STRING]
|
||||
l_types : STRING
|
||||
do
|
||||
l_types := "en-*,fr"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("match using a type wildcard", parser.best_match (mime_types_supported, "en-gb").same_string ("en-*"))
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
parser : LANGUAGE_PARSE
|
||||
|
||||
end
|
||||
|
||||
|
||||
129
library/network/protocol/CONNEG/test/mime_parser_test.e
Normal file
129
library/network/protocol/CONNEG/test/mime_parser_test.e
Normal file
@@ -0,0 +1,129 @@
|
||||
note
|
||||
description: "[
|
||||
Eiffel tests that can be executed by testing tool.
|
||||
]"
|
||||
author: "EiffelStudio test wizard"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
testing: "type/manual"
|
||||
|
||||
class
|
||||
MIME_PARSER_TEST
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
redefine
|
||||
on_prepare
|
||||
end
|
||||
|
||||
feature {NONE} -- Events
|
||||
|
||||
on_prepare
|
||||
-- Called after all initializations in `default_create'.
|
||||
do
|
||||
create parser
|
||||
end
|
||||
|
||||
feature -- Test routines
|
||||
|
||||
test_parse_media_range
|
||||
do
|
||||
assert ("Expected ('application', 'xml', {'q':'1',})", parser.parse_media_range("application/xml;q=1").out.same_string("('application', 'xml', {'q':'1.0',})") )
|
||||
|
||||
assert ("Expected ('application', 'xml', {'q':'1',})", parser.parse_media_range("application/xml").out.same_string("('application', 'xml', {'q':'1.0',})") )
|
||||
assert ("Expected ('application', 'xml', {'q':'1',})", parser.parse_media_range("application/xml;q=").out.same_string("('application', 'xml', {'q':'1.0',})") )
|
||||
assert ("Expected ('application', 'xml', {'q':'1',})", parser.parse_media_range("application/xml ; q=").out.same_string("('application', 'xml', {'q':'1.0',})") )
|
||||
assert ("Expected ('application', 'xml', {'q':'1','b':'other',})", parser.parse_media_range("application/xml ; q=1;b=other").out.same_string("('application', 'xml', {'q':'1.0','b':'other',})") )
|
||||
assert ("Expected ('application', 'xml', {'q':'1','b':'other',})", parser.parse_media_range("application/xml ; q=2;b=other").out.same_string("('application', 'xml', {'q':'1.0','b':'other',})") )
|
||||
-- Accept header that includes *
|
||||
assert ("Expected ('*', '*', {'q':'.2',})", parser.parse_media_range(" *; q=.2").out.same_string("('*', '*', {'q':'.2',})"))
|
||||
end
|
||||
|
||||
|
||||
test_RFC2616_example
|
||||
local
|
||||
accept : STRING
|
||||
do
|
||||
accept := "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5";
|
||||
assert ("Expected 1.0", 1.0 = parser.quality ("text/html;level=1", accept))
|
||||
assert ("Expected 0.3", 0.3 = parser.quality ("text/plain", accept))
|
||||
assert ("Expected 0.7", 0.7 = parser.quality ("text/html", accept))
|
||||
assert ("Expected 0.5", 0.5 = parser.quality ("image/jpeg", accept))
|
||||
assert ("Expected 0.4", 0.4 = parser.quality ("text/html;level=2", accept))
|
||||
assert ("Expected 0.7", 0.7 = parser.quality ("text/html;level=3", accept))
|
||||
end
|
||||
|
||||
|
||||
test_best_match
|
||||
local
|
||||
mime_types_supported : LIST [STRING]
|
||||
l_types : STRING
|
||||
do
|
||||
l_types := "application/xbel+xml,application/xml"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Expected application/xbel+xml", parser.best_match (mime_types_supported, "application/xbel+xml").same_string ("application/xbel+xml"))
|
||||
assert ("Direct match with a q parameter", parser.best_match (mime_types_supported, "application/xbel+xml;q=1").same_string ("application/xbel+xml"))
|
||||
assert ("Direct match second choice with a q parameter", parser.best_match (mime_types_supported, "application/xml;q=1").same_string ("application/xml"))
|
||||
assert ("Direct match using a subtype wildcard", parser.best_match (mime_types_supported, "application/*;q=1").is_equal ("application/xbel+xml"))
|
||||
assert ("Match using a type wildcard", parser.best_match (mime_types_supported, "*/*").same_string ("application/xbel+xml"))
|
||||
|
||||
l_types := "application/xbel+xml,text/xml"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Match using a type versus a lower weighted subtype", parser.best_match (mime_types_supported, "text/*;q=0.5,*/*;q=0.1").same_string ("text/xml"))
|
||||
assert ("Fail to match anything",parser.best_match (mime_types_supported, "text/html,application/atom+xml; q=0.9").same_string (""))
|
||||
|
||||
l_types := "application/json,text/html"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Common Ajax scenario", parser.best_match (mime_types_supported, "application/json,text/javascript, */*").same_string ("application/json"))
|
||||
assert ("Common Ajax scenario,verify fitness ordering", parser.best_match (mime_types_supported, "application/json,text/javascript, */*").same_string ("application/json"))
|
||||
|
||||
l_types := "text/html,application/atom+xml;q=1.0,application/xml;q=0.6"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Match default text/html at first position", parser.best_match (mime_types_supported, "text/html;q=1.0,*/*;q=0.1,application/xml").same_string ("text/html"))
|
||||
|
||||
l_types := "application/atom+xml;q=1.0,application/xml;q=0.6,text/html"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Match default text/html at last position", parser.best_match (mime_types_supported, "text/html;q=1.0,*/*;q=0.1,application/xml").same_string ("text/html"))
|
||||
|
||||
l_types := "application/atom+xml;q=1.0,application/xml,text/html"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Match first top quality and fitness", parser.best_match (mime_types_supported, "text/html;q=1.0,*/*;q=0.1,application/xml").same_string ("text/html"))
|
||||
|
||||
l_types := "application/atom+xml;q=1.0,application/xml,text/html"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Test 1", parser.best_match (mime_types_supported, "text/html;q=1.0,*/*;q=0.1,application/xml;q=0.9").same_string ("text/html"))
|
||||
|
||||
l_types := "application/atom+xml;q=1.0,application/xml,text/html"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Test 1", parser.best_match (mime_types_supported, "text/html,*/*;q=0.1,application/xml").same_string ("text/html"))
|
||||
|
||||
l_types := "application/atom+xml;q=1.0,application/xml,text/html"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Test 2", parser.best_match (mime_types_supported, "application/xml,text/html,*/*;q=0.1").same_string ("application/xml"))
|
||||
|
||||
l_types := "text/html,application/xml;q=0.6"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("Test 2", parser.best_match (mime_types_supported, "application/atom+xml;q=1.0, application/xml;q=0.6, text/html").same_string ("text/html"))
|
||||
|
||||
end
|
||||
|
||||
|
||||
test_support_wildcard
|
||||
local
|
||||
mime_types_supported : LIST[STRING]
|
||||
l_types : STRING
|
||||
do
|
||||
l_types := "image/*,application/xml"
|
||||
mime_types_supported := l_types.split(',')
|
||||
assert ("match using a type wildcard", parser.best_match (mime_types_supported, "image/png").same_string ("image/*"))
|
||||
assert ("match using a wildcard for both requested and supported", parser.best_match (mime_types_supported, "image/*").same_string ("image/*"))
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
parser : MIME_PARSE
|
||||
|
||||
end
|
||||
|
||||
|
||||
19
library/network/protocol/CONNEG/test/test-safe.ecf
Normal file
19
library/network/protocol/CONNEG/test/test-safe.ecf
Normal file
@@ -0,0 +1,19 @@
|
||||
<?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">
|
||||
<target name="test">
|
||||
<root class="APPLICATION" feature="make"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
<exclude>/.git$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true">
|
||||
<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"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||
<cluster name="test" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
19
library/network/protocol/CONNEG/test/test.ecf
Normal file
19
library/network/protocol/CONNEG/test/test.ecf
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="test" uuid="7860561C-779A-4E45-A7B9-06A1E0E984E8">
|
||||
<target name="test">
|
||||
<root class="APPLICATION" feature="make"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
<exclude>/.git$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="conneg" location="..\conneg.ecf"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing.ecf"/>
|
||||
<cluster name="test" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
1
library/network/protocol/CONNEG/test/test.rc
Normal file
1
library/network/protocol/CONNEG/test/test.rc
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
8
library/network/protocol/http/README.md
Normal file
8
library/network/protocol/http/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# HTTP library
|
||||
|
||||
## Overview
|
||||
|
||||
## Usage
|
||||
|
||||
## Examples
|
||||
|
||||
17
library/network/protocol/http/http-safe.ecf
Normal file
17
library/network/protocol/http/http-safe.ecf
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="http" uuid="F8BE3C55-88E8-4103-A936-B1E5CB1D330E" library_target="http">
|
||||
<target name="http">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
18
library/network/protocol/http/http.ecf
Normal file
18
library/network/protocol/http/http.ecf
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="http" uuid="F8BE3C55-88E8-4103-A936-B1E5CB1D330E" library_target="http">
|
||||
<target name="http">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" syntax="provisional">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
10
library/network/protocol/http/license.lic
Normal file
10
library/network/protocol/http/license.lic
Normal file
@@ -0,0 +1,10 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, 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
|
||||
]"
|
||||
48
library/network/protocol/http/src/http_constants.e
Normal file
48
library/network/protocol/http/src/http_constants.e
Normal file
@@ -0,0 +1,48 @@
|
||||
note
|
||||
description: "[
|
||||
Constants class providing most common constants used in HTTP communication
|
||||
]"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_CONSTANTS
|
||||
|
||||
inherit
|
||||
HTTP_MIME_TYPES
|
||||
|
||||
HTTP_HEADER_NAMES
|
||||
|
||||
HTTP_STATUS_CODE
|
||||
|
||||
HTTP_REQUEST_METHODS
|
||||
|
||||
feature -- Ports
|
||||
|
||||
default_http_port: INTEGER = 80
|
||||
default_https_port: INTEGER = 443
|
||||
|
||||
feature -- Server, header
|
||||
|
||||
http_version_1_0: STRING = "HTTP/1.0"
|
||||
http_version_1_1: STRING = "HTTP/1.1"
|
||||
|
||||
feature -- Misc
|
||||
|
||||
crlf: STRING = "%R%N"
|
||||
|
||||
default_bufsize: INTEGER = 16384 --| 16K
|
||||
|
||||
note
|
||||
copyright: "2011-2011, 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
|
||||
46
library/network/protocol/http/src/http_content_type.e
Normal file
46
library/network/protocol/http/src/http_content_type.e
Normal file
@@ -0,0 +1,46 @@
|
||||
note
|
||||
description: "[
|
||||
This class is to represent the CONTENT_TYPE value
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_CONTENT_TYPE
|
||||
|
||||
inherit
|
||||
HTTP_MEDIA_TYPE
|
||||
|
||||
create
|
||||
make,
|
||||
make_from_string
|
||||
|
||||
feature -- Access
|
||||
|
||||
charset_parameter: detachable READABLE_STRING_8
|
||||
do
|
||||
if has_charset_parameter then
|
||||
Result := parameter (charset_parameter_name)
|
||||
end
|
||||
end
|
||||
|
||||
has_charset_parameter: BOOLEAN
|
||||
do
|
||||
Result := has_parameter (charset_parameter_name)
|
||||
end
|
||||
|
||||
feature -- Constant
|
||||
|
||||
charset_parameter_name: STRING_8 = "charset"
|
||||
|
||||
note
|
||||
copyright: "2011-2012, 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
|
||||
71
library/network/protocol/http/src/http_date_time_utilities.e
Normal file
71
library/network/protocol/http/src/http_date_time_utilities.e
Normal file
@@ -0,0 +1,71 @@
|
||||
note
|
||||
description: "Summary description for {HTTP_DATE_TIME_UTILITIES}."
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_DATE_TIME_UTILITIES
|
||||
|
||||
feature -- Access
|
||||
|
||||
now_utc: DATE_TIME
|
||||
do
|
||||
create Result.make_now_utc
|
||||
end
|
||||
|
||||
epoch: DATE_TIME
|
||||
once ("THREAD")
|
||||
create Result.make_from_epoch (0)
|
||||
end
|
||||
|
||||
feature -- Unix time stamp
|
||||
|
||||
unix_time_stamp (dt: detachable DATE_TIME): INTEGER_64
|
||||
-- Unix time stamp from `dt' if attached or from epoch is detached
|
||||
local
|
||||
l_date_time: DATE_TIME
|
||||
do
|
||||
if dt /= Void then
|
||||
l_date_time := dt
|
||||
else
|
||||
l_date_time := now_utc
|
||||
end
|
||||
Result := l_date_time.definite_duration (epoch).seconds_count
|
||||
end
|
||||
|
||||
fine_unix_time_stamp (dt: detachable DATE_TIME): DOUBLE
|
||||
-- Fine unix time stamp from `dt' if attached or from epoch is detached
|
||||
local
|
||||
l_date_time: DATE_TIME
|
||||
do
|
||||
if dt /= Void then
|
||||
l_date_time := dt
|
||||
else
|
||||
l_date_time := now_utc
|
||||
end
|
||||
Result := l_date_time.definite_duration (epoch).fine_seconds_count
|
||||
end
|
||||
|
||||
feature -- Unix time stamp conversion
|
||||
|
||||
unix_time_stamp_to_date_time (i64: INTEGER_64): DATE_TIME
|
||||
-- Date time related to `i64'
|
||||
do
|
||||
create Result.make_from_epoch (i64.as_integer_32)
|
||||
ensure
|
||||
same_unix_time_stamp: unix_time_stamp (Result) = i64
|
||||
end
|
||||
|
||||
;note
|
||||
copyright: "Copyright (c) 1984-2011, 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
|
||||
@@ -0,0 +1,238 @@
|
||||
note
|
||||
description: "[
|
||||
Various common MIME types for file extensions
|
||||
|
||||
See also for longer list and description
|
||||
http://www.webmaster-toolkit.com/mime-types.shtml
|
||||
|
||||
Please suggest missing entries
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_FILE_EXTENSION_MIME_MAPPING
|
||||
|
||||
inherit
|
||||
ANY
|
||||
|
||||
HTTP_MIME_TYPES
|
||||
export
|
||||
{NONE} all
|
||||
end
|
||||
|
||||
create
|
||||
make_empty,
|
||||
make_default,
|
||||
make_from_string,
|
||||
make_from_file
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_empty (n: INTEGER)
|
||||
-- Create with no mapping
|
||||
-- but one can use `map' to add new mapping
|
||||
do
|
||||
create mapping.make (n)
|
||||
mapping.compare_objects
|
||||
end
|
||||
|
||||
make_default
|
||||
-- Create with default limited mapping
|
||||
-- One can use `map' to add new mapping
|
||||
local
|
||||
m: like mapping
|
||||
do
|
||||
create m.make (40)
|
||||
mapping := m
|
||||
m.compare_objects
|
||||
m.force (text_css, "css")
|
||||
m.force (text_html, "html")
|
||||
m.force (text_xml, "xml")
|
||||
m.force (application_json, "json")
|
||||
m.force (application_javascript, "js")
|
||||
m.force (application_rss_xml, "rss")
|
||||
m.force (application_atom_xml, "atom")
|
||||
m.force (image_x_ico, "ico")
|
||||
m.force (image_gif, "gif")
|
||||
m.force (image_jpeg, "jpeg")
|
||||
m.force (image_jpg, "jpg")
|
||||
m.force (image_png, "png")
|
||||
m.force (application_zip, "zip")
|
||||
m.force (application_x_bzip, "bz")
|
||||
m.force (application_x_bzip2, "bz2")
|
||||
m.force (application_x_gzip, "gz")
|
||||
m.force (application_x_gzip, "gzip")
|
||||
m.force (application_x_tar, "tar")
|
||||
m.force (application_x_compressed, "tgz")
|
||||
m.force (application_postscript, "ps")
|
||||
m.force (application_pdf, "pdf")
|
||||
m.force (application_x_shockwave_flash, "swf")
|
||||
m.force (text_plain, "conf")
|
||||
m.force (text_plain, "log")
|
||||
m.force (text_plain, "text")
|
||||
m.force (text_plain, "txt")
|
||||
end
|
||||
|
||||
make_from_file (fn: READABLE_STRING_8)
|
||||
-- Create with mime.types file
|
||||
-- One can use `map' to add new mapping
|
||||
local
|
||||
f: RAW_FILE
|
||||
do
|
||||
create f.make (fn)
|
||||
if f.exists and then f.is_readable then
|
||||
make_empty (50)
|
||||
f.open_read
|
||||
from
|
||||
f.read_line
|
||||
until
|
||||
f.exhausted or f.end_of_file
|
||||
loop
|
||||
add_mapping_line (f.last_string)
|
||||
f.read_line
|
||||
end
|
||||
f.close
|
||||
else
|
||||
make_empty (0)
|
||||
end
|
||||
end
|
||||
|
||||
make_from_string (t: READABLE_STRING_8)
|
||||
-- Set mapping from multiline string `t'
|
||||
-- line should be formatted as in http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
||||
--| # is a comment
|
||||
--| mime-type space(s) extensions
|
||||
local
|
||||
i,j,n: INTEGER
|
||||
do
|
||||
make_empty (10)
|
||||
n := t.count
|
||||
if n > 0 then
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i = 0 or i > n
|
||||
loop
|
||||
j := t.index_of ('%N', i)
|
||||
if j > 0 then
|
||||
add_mapping_line (t.substring (i, j - 1))
|
||||
i := j + 1
|
||||
else
|
||||
add_mapping_line (t.substring (i, n))
|
||||
i := 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
mime_type (ext: READABLE_STRING_8): detachable READABLE_STRING_8
|
||||
-- Mime type for extension `ext'
|
||||
do
|
||||
Result := mapping.item (ext.as_lower)
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
map (e: READABLE_STRING_8; t: READABLE_STRING_8)
|
||||
-- Add mapping extension `e' to mime type `t'
|
||||
do
|
||||
mapping.force (t, e.as_lower)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
add_mapping_line (t: READABLE_STRING_8)
|
||||
local
|
||||
i,j,n: INTEGER
|
||||
l_type, l_ext: READABLE_STRING_8
|
||||
do
|
||||
n := t.count
|
||||
if n > 0 then
|
||||
-- ignore blanks
|
||||
i := next_non_blank_position (t, i)
|
||||
if i > 0 then
|
||||
if t[i] = '#' then
|
||||
--| ignore
|
||||
else
|
||||
j := next_blank_position (t, i)
|
||||
if j > i then
|
||||
l_type := t.substring (i, j - 1)
|
||||
from
|
||||
until
|
||||
i = 0
|
||||
loop
|
||||
i := next_non_blank_position (t, j)
|
||||
if i > 0 then
|
||||
j := next_blank_position (t, i)
|
||||
if j = 0 then
|
||||
l_ext := t.substring (i, n)
|
||||
i := 0
|
||||
else
|
||||
l_ext := t.substring (i, j - 1)
|
||||
i := j
|
||||
end
|
||||
map (l_ext, l_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
next_blank_position (s: READABLE_STRING_8; p: INTEGER): INTEGER
|
||||
local
|
||||
i, n: INTEGER
|
||||
do
|
||||
n := s.count
|
||||
from
|
||||
i := p + 1
|
||||
until
|
||||
i > n or s[i].is_space
|
||||
loop
|
||||
i := i + 1
|
||||
end
|
||||
if i <= n then
|
||||
Result := i
|
||||
end
|
||||
end
|
||||
|
||||
next_non_blank_position (s: READABLE_STRING_8; p: INTEGER): INTEGER
|
||||
local
|
||||
i, n: INTEGER
|
||||
do
|
||||
n := s.count
|
||||
from
|
||||
i := p + 1
|
||||
until
|
||||
i > n or not s[i].is_space
|
||||
loop
|
||||
i := i + 1
|
||||
end
|
||||
if i <= n then
|
||||
Result := i
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
feature {NONE} -- Extension MIME mapping
|
||||
|
||||
mapping: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
|
||||
|
||||
invariant
|
||||
mapping_keys_are_lowercase: across mapping as c all c.key.same_string (c.key.as_lower) end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, 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
|
||||
86
library/network/protocol/http/src/http_format_constants.e
Normal file
86
library/network/protocol/http/src/http_format_constants.e
Normal file
@@ -0,0 +1,86 @@
|
||||
note
|
||||
description: "Summary description for {HTTP_FORMAT_CONSTANTS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_FORMAT_CONSTANTS
|
||||
|
||||
feature -- Id
|
||||
|
||||
json: INTEGER = 0x1
|
||||
|
||||
xml: INTEGER = 0x2
|
||||
|
||||
text: INTEGER = 0x4
|
||||
|
||||
html: INTEGER = 0x8
|
||||
|
||||
rss: INTEGER = 0x10
|
||||
|
||||
atom: INTEGER = 0x20
|
||||
|
||||
feature -- Name
|
||||
|
||||
json_name: STRING = "json"
|
||||
|
||||
xml_name: STRING = "xml"
|
||||
|
||||
text_name: STRING = "text"
|
||||
|
||||
html_name: STRING = "html"
|
||||
|
||||
rss_name: STRING = "rss"
|
||||
|
||||
atom_name: STRING = "atom"
|
||||
|
||||
empty_name: STRING = ""
|
||||
|
||||
feature -- Query
|
||||
|
||||
format_id (a_id: READABLE_STRING_GENERAL): INTEGER
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
s := a_id.as_string_8.as_lower
|
||||
if s.same_string (json_name) then
|
||||
Result := json
|
||||
elseif s.same_string (xml_name) then
|
||||
Result := xml
|
||||
elseif s.same_string (text_name) then
|
||||
Result := text
|
||||
elseif s.same_string (html_name) then
|
||||
Result := html
|
||||
elseif s.same_string (rss_name) then
|
||||
Result := rss
|
||||
elseif s.same_string (atom_name) then
|
||||
Result := atom
|
||||
end
|
||||
end
|
||||
|
||||
format_name (a_id: INTEGER): READABLE_STRING_8
|
||||
do
|
||||
inspect a_id
|
||||
when json then Result := json_name
|
||||
when xml then Result := xml_name
|
||||
when text then Result := text_name
|
||||
when html then Result := html_name
|
||||
when rss then Result := rss_name
|
||||
when atom then Result := atom_name
|
||||
else Result := empty_name
|
||||
end
|
||||
ensure
|
||||
result_is_lower_case: Result /= Void and then Result.as_lower ~ Result
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, 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
|
||||
689
library/network/protocol/http/src/http_header.e
Normal file
689
library/network/protocol/http/src/http_header.e
Normal file
@@ -0,0 +1,689 @@
|
||||
note
|
||||
description: "[
|
||||
The class provides an easy way to build HTTP header.
|
||||
|
||||
You will also find some helper feature to help coding most common usage
|
||||
|
||||
Please, have a look at constants classes such as
|
||||
HTTP_MIME_TYPES
|
||||
HTTP_HEADER_NAMES
|
||||
HTTP_STATUS_CODE
|
||||
HTTP_REQUEST_METHODS
|
||||
(or HTTP_CONSTANTS which groups them for convenience)
|
||||
|
||||
Note the return status code is not part of the HTTP header
|
||||
]"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_HEADER
|
||||
|
||||
inherit
|
||||
ANY
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_count,
|
||||
make_from_array,
|
||||
make_from_header,
|
||||
make_from_raw_header_data
|
||||
|
||||
convert
|
||||
make_from_array ({ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]]}),
|
||||
string: {READABLE_STRING_8, STRING_8}
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
-- Initialize current
|
||||
do
|
||||
make_with_count (3)
|
||||
end
|
||||
|
||||
make_with_count (n: INTEGER)
|
||||
-- Make with a capacity of `n' header entries
|
||||
do
|
||||
create {ARRAYED_LIST [READABLE_STRING_8]} headers.make (n)
|
||||
end
|
||||
|
||||
make_from_array (a_headers: ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]])
|
||||
-- Create HEADER from array of pair (key,value)
|
||||
do
|
||||
if a_headers.is_empty then
|
||||
make_with_count (0)
|
||||
else
|
||||
make_with_count (a_headers.count)
|
||||
append_array (a_headers)
|
||||
end
|
||||
end
|
||||
|
||||
make_from_header (a_header: HTTP_HEADER)
|
||||
-- Create Current from existing HEADER `a_header'
|
||||
do
|
||||
make_with_count (a_header.headers.count)
|
||||
append_header_object (a_header)
|
||||
end
|
||||
|
||||
make_from_raw_header_data (h: READABLE_STRING_8)
|
||||
-- Create Current from raw header data
|
||||
local
|
||||
line : detachable STRING
|
||||
lines: LIST [READABLE_STRING_8]
|
||||
do
|
||||
lines := h.split ('%N')
|
||||
make_with_count (lines.count)
|
||||
across
|
||||
lines as c
|
||||
loop
|
||||
line := c.item
|
||||
if not line.is_empty then
|
||||
if line [line.count] = '%R' then
|
||||
line.remove_tail (1)
|
||||
end
|
||||
add_header (line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Recycle
|
||||
|
||||
recycle
|
||||
-- Recycle current object
|
||||
do
|
||||
headers.wipe_out
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
headers: ARRAYED_LIST [READABLE_STRING_8]
|
||||
-- Header's lines
|
||||
|
||||
string: STRING_8
|
||||
-- String representation of the headers
|
||||
local
|
||||
l_headers: like headers
|
||||
do
|
||||
l_headers := headers
|
||||
if not l_headers.is_empty then
|
||||
create Result.make (l_headers.count * 32)
|
||||
across
|
||||
l_headers as c
|
||||
loop
|
||||
append_line_to (c.item, Result)
|
||||
end
|
||||
else
|
||||
create Result.make_empty
|
||||
end
|
||||
ensure
|
||||
result_has_ending_cr_lf: Result.count >= 2 implies Result.substring (Result.count - 1, Result.count).same_string ("%R%N")
|
||||
result_has_single_ending_cr_lf: Result.count >= 4 implies not Result.substring (Result.count - 3, Result.count).same_string ("%R%N%R%N")
|
||||
end
|
||||
|
||||
to_name_value_iterable: ITERABLE [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]
|
||||
local
|
||||
res: ARRAYED_LIST [TUPLE [READABLE_STRING_8, READABLE_STRING_8]]
|
||||
do
|
||||
create res.make (headers.count)
|
||||
across
|
||||
headers as c
|
||||
loop
|
||||
if attached header_name_value (c.item) as tu then
|
||||
res.extend (tu)
|
||||
end
|
||||
end
|
||||
Result := res
|
||||
end
|
||||
|
||||
feature -- Header: filling
|
||||
|
||||
append_array (a_headers: ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]])
|
||||
-- Append array of key,value headers
|
||||
do
|
||||
headers.grow (headers.count + a_headers.count)
|
||||
across
|
||||
a_headers as c
|
||||
loop
|
||||
put_header_key_value (c.item.key, c.item.value)
|
||||
end
|
||||
end
|
||||
|
||||
append_header_object (h: HTTP_HEADER)
|
||||
-- Append headers from `h'
|
||||
do
|
||||
headers.grow (headers.count + h.headers.count)
|
||||
across
|
||||
h.headers as c
|
||||
loop
|
||||
add_header (c.item.string)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Header change: general
|
||||
|
||||
add_header (h: READABLE_STRING_8)
|
||||
-- Add header `h'
|
||||
-- if it already exists, there will be multiple header with same name
|
||||
-- which can also be valid
|
||||
require
|
||||
h_not_empty: not h.is_empty
|
||||
do
|
||||
headers.force (h)
|
||||
end
|
||||
|
||||
put_header (h: READABLE_STRING_8)
|
||||
-- Add header `h' or replace existing header of same header name
|
||||
require
|
||||
h_not_empty: not h.is_empty
|
||||
do
|
||||
force_header_by_name (header_name_colon (h), h)
|
||||
end
|
||||
|
||||
add_header_key_value (k,v: READABLE_STRING_8)
|
||||
-- Add header `k:v', or replace existing header of same header name/key
|
||||
do
|
||||
add_header (k + colon_space + v)
|
||||
end
|
||||
|
||||
put_header_key_value (k,v: READABLE_STRING_8)
|
||||
-- Add header `k:v', or replace existing header of same header name/key
|
||||
do
|
||||
put_header (k + colon_space + v)
|
||||
end
|
||||
|
||||
feature -- Content related header
|
||||
|
||||
put_content_type (t: READABLE_STRING_8)
|
||||
do
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t)
|
||||
end
|
||||
|
||||
add_content_type (t: READABLE_STRING_8)
|
||||
-- same as `put_content_type', but allow multiple definition of "Content-Type"
|
||||
do
|
||||
add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t)
|
||||
end
|
||||
|
||||
put_content_type_with_parameters (t: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
|
||||
local
|
||||
s: STRING_8
|
||||
do
|
||||
if a_params /= Void and then not a_params.is_empty then
|
||||
create s.make_from_string (t)
|
||||
across
|
||||
a_params as p
|
||||
loop
|
||||
if attached p.item as nv then
|
||||
s.append_character (';')
|
||||
s.append_character (' ')
|
||||
s.append (nv.name)
|
||||
s.append_character ('=')
|
||||
s.append_character ('%"')
|
||||
s.append (nv.value)
|
||||
s.append_character ('%"')
|
||||
end
|
||||
end
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s)
|
||||
else
|
||||
put_content_type (t)
|
||||
end
|
||||
end
|
||||
|
||||
add_content_type_with_parameters (t: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
|
||||
local
|
||||
s: STRING_8
|
||||
do
|
||||
if a_params /= Void and then not a_params.is_empty then
|
||||
create s.make_from_string (t)
|
||||
across
|
||||
a_params as p
|
||||
loop
|
||||
if attached p.item as nv then
|
||||
s.append_character (';')
|
||||
s.append_character (' ')
|
||||
s.append (nv.name)
|
||||
s.append_character ('=')
|
||||
s.append_character ('%"')
|
||||
s.append (nv.value)
|
||||
s.append_character ('%"')
|
||||
end
|
||||
end
|
||||
add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s)
|
||||
else
|
||||
add_content_type (t)
|
||||
end
|
||||
end
|
||||
|
||||
put_content_type_with_charset (t: READABLE_STRING_8; c: READABLE_STRING_8)
|
||||
do
|
||||
put_content_type_with_parameters (t, <<["charset", c]>>)
|
||||
end
|
||||
|
||||
add_content_type_with_charset (t: READABLE_STRING_8; c: READABLE_STRING_8)
|
||||
-- same as `put_content_type_with_charset', but allow multiple definition of "Content-Type"
|
||||
do
|
||||
add_content_type_with_parameters (t, <<["charset", c]>>)
|
||||
end
|
||||
|
||||
put_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8)
|
||||
do
|
||||
put_content_type_with_parameters (t, <<["name", n]>>)
|
||||
end
|
||||
|
||||
add_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8)
|
||||
-- same as `put_content_type_with_name', but allow multiple definition of "Content-Type"
|
||||
do
|
||||
add_content_type_with_parameters (t, <<["name", n]>>)
|
||||
end
|
||||
|
||||
put_content_length (n: INTEGER)
|
||||
do
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_length, n.out)
|
||||
end
|
||||
|
||||
put_content_transfer_encoding (a_mechanism: READABLE_STRING_8)
|
||||
-- Put "Content-Transfer-Encoding" header with for instance "binary"
|
||||
--| encoding := "Content-Transfer-Encoding" ":" mechanism
|
||||
--|
|
||||
--| mechanism := "7bit" ; case-insensitive
|
||||
--| / "quoted-printable"
|
||||
--| / "base64"
|
||||
--| / "8bit"
|
||||
--| / "binary"
|
||||
--| / x-token
|
||||
do
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_transfer_encoding, a_mechanism)
|
||||
end
|
||||
|
||||
put_transfer_encoding (a_enc: READABLE_STRING_8)
|
||||
-- Put "Transfer-Encoding" header with for instance "chunked"
|
||||
do
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_transfer_encoding, a_enc)
|
||||
end
|
||||
|
||||
put_transfer_encoding_binary
|
||||
-- Put "Transfer-Encoding: binary" header
|
||||
do
|
||||
put_transfer_encoding (str_binary)
|
||||
end
|
||||
|
||||
put_transfer_encoding_chunked
|
||||
-- Put "Transfer-Encoding: chunked" header
|
||||
do
|
||||
put_transfer_encoding (str_chunked)
|
||||
end
|
||||
|
||||
put_content_disposition (a_type: READABLE_STRING_8; a_params: detachable READABLE_STRING_8)
|
||||
-- Put "Content-Disposition" header
|
||||
--| See RFC2183
|
||||
--| disposition := "Content-Disposition" ":"
|
||||
--| disposition-type
|
||||
--| *(";" disposition-parm)
|
||||
--| disposition-type := "inline"
|
||||
--| / "attachment"
|
||||
--| / extension-token
|
||||
--| ; values are not case-sensitive
|
||||
--| disposition-parm := filename-parm
|
||||
--| / creation-date-parm
|
||||
--| / modification-date-parm
|
||||
--| / read-date-parm
|
||||
--| / size-parm
|
||||
--| / parameter
|
||||
--| filename-parm := "filename" "=" value
|
||||
--| creation-date-parm := "creation-date" "=" quoted-date-time
|
||||
--| modification-date-parm := "modification-date" "=" quoted-date-time
|
||||
--| read-date-parm := "read-date" "=" quoted-date-time
|
||||
--| size-parm := "size" "=" 1*DIGIT
|
||||
--| quoted-date-time := quoted-string
|
||||
--| ; contents MUST be an RFC 822 `date-time'
|
||||
--| ; numeric timezones (+HHMM or -HHMM) MUST be used
|
||||
do
|
||||
if a_params /= Void then
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type + semi_colon_space + a_params)
|
||||
else
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Content-type helpers
|
||||
|
||||
put_content_type_text_css do put_content_type ({HTTP_MIME_TYPES}.text_css) end
|
||||
put_content_type_text_csv do put_content_type ({HTTP_MIME_TYPES}.text_csv) end
|
||||
put_content_type_text_html do put_content_type ({HTTP_MIME_TYPES}.text_html) end
|
||||
put_content_type_text_javascript do put_content_type ({HTTP_MIME_TYPES}.text_javascript) end
|
||||
put_content_type_text_json do put_content_type ({HTTP_MIME_TYPES}.text_json) end
|
||||
put_content_type_text_plain do put_content_type ({HTTP_MIME_TYPES}.text_plain) end
|
||||
put_content_type_text_xml do put_content_type ({HTTP_MIME_TYPES}.text_xml) end
|
||||
|
||||
put_content_type_application_json do put_content_type ({HTTP_MIME_TYPES}.application_json) end
|
||||
put_content_type_application_javascript do put_content_type ({HTTP_MIME_TYPES}.application_javascript) end
|
||||
put_content_type_application_zip do put_content_type ({HTTP_MIME_TYPES}.application_zip) end
|
||||
|
||||
put_content_type_image_gif do put_content_type ({HTTP_MIME_TYPES}.image_gif) end
|
||||
put_content_type_image_png do put_content_type ({HTTP_MIME_TYPES}.image_png) end
|
||||
put_content_type_image_jpg do put_content_type ({HTTP_MIME_TYPES}.image_jpg) end
|
||||
put_content_type_image_svg_xml do put_content_type ({HTTP_MIME_TYPES}.image_svg_xml) end
|
||||
|
||||
put_content_type_message_http do put_content_type ({HTTP_MIME_TYPES}.message_http) end
|
||||
|
||||
put_content_type_multipart_mixed do put_content_type ({HTTP_MIME_TYPES}.multipart_mixed) end
|
||||
put_content_type_multipart_alternative do put_content_type ({HTTP_MIME_TYPES}.multipart_alternative) end
|
||||
put_content_type_multipart_related do put_content_type ({HTTP_MIME_TYPES}.multipart_related) end
|
||||
put_content_type_multipart_form_data do put_content_type ({HTTP_MIME_TYPES}.multipart_form_data) end
|
||||
put_content_type_multipart_signed do put_content_type ({HTTP_MIME_TYPES}.multipart_signed) end
|
||||
put_content_type_multipart_encrypted do put_content_type ({HTTP_MIME_TYPES}.multipart_encrypted) end
|
||||
put_content_type_application_x_www_form_encoded do put_content_type ({HTTP_MIME_TYPES}.application_x_www_form_encoded) end
|
||||
|
||||
feature -- Date
|
||||
|
||||
put_date (s: READABLE_STRING_8)
|
||||
-- Put "Date: " header
|
||||
do
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_date, s)
|
||||
end
|
||||
|
||||
put_current_date
|
||||
-- Put current date time with "Date" header
|
||||
do
|
||||
put_utc_date (create {DATE_TIME}.make_now_utc)
|
||||
end
|
||||
|
||||
put_utc_date (dt: DATE_TIME)
|
||||
-- Put UTC date time `dt' with "Date" header
|
||||
do
|
||||
put_date (date_to_rfc1123_http_date_format (dt))
|
||||
end
|
||||
|
||||
put_last_modified (dt: DATE_TIME)
|
||||
-- Put UTC date time `dt' with "Date" header
|
||||
do
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_last_modified, date_to_rfc1123_http_date_format (dt))
|
||||
end
|
||||
|
||||
feature -- Others
|
||||
|
||||
put_expires (n: INTEGER)
|
||||
do
|
||||
put_header_key_value ("Expires", n.out)
|
||||
end
|
||||
|
||||
put_cache_control (s: READABLE_STRING_8)
|
||||
-- `s' could be for instance "no-cache, must-revalidate"
|
||||
do
|
||||
put_header_key_value ("Cache-Control", s)
|
||||
end
|
||||
|
||||
put_pragma (s: READABLE_STRING_8)
|
||||
do
|
||||
put_header_key_value ("Pragma", s)
|
||||
end
|
||||
|
||||
put_pragma_no_cache
|
||||
do
|
||||
put_pragma ("no-cache")
|
||||
end
|
||||
|
||||
feature -- Redirection
|
||||
|
||||
put_location (a_location: READABLE_STRING_8)
|
||||
-- Tell the client the new location `a_location'
|
||||
require
|
||||
a_location_valid: not a_location.is_empty
|
||||
do
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_location, a_location)
|
||||
end
|
||||
|
||||
put_refresh (a_location: READABLE_STRING_8; a_timeout_in_seconds: INTEGER)
|
||||
-- Tell the client to refresh page with `a_location' after `a_timeout_in_seconds' in seconds
|
||||
require
|
||||
a_location_valid: not a_location.is_empty
|
||||
do
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_refresh, a_timeout_in_seconds.out + "; url=" + a_location)
|
||||
end
|
||||
|
||||
feature -- Cookie
|
||||
|
||||
put_cookie (key, value: READABLE_STRING_8; expiration, path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN)
|
||||
-- Set a cookie on the client's machine
|
||||
-- with key 'key' and value 'value'.
|
||||
-- Note: you should avoid using "localhost" as `domain' for local cookies
|
||||
-- since they are not always handled by browser (for instance Chrome)
|
||||
require
|
||||
make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty)
|
||||
domain_without_port_info: domain /= Void implies domain.index_of (':', 1) = 0
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
s := {HTTP_HEADER_NAMES}.header_set_cookie + colon_space + key + "=" + value
|
||||
if
|
||||
domain /= Void and then not domain.same_string ("localhost")
|
||||
then
|
||||
s.append ("; Domain=" + domain)
|
||||
end
|
||||
if path /= Void then
|
||||
s.append ("; Path=" + path)
|
||||
end
|
||||
if expiration /= Void then
|
||||
s.append ("; Expires=" + expiration)
|
||||
end
|
||||
if secure then
|
||||
s.append ("; Secure")
|
||||
end
|
||||
if http_only then
|
||||
s.append ("; HttpOnly")
|
||||
end
|
||||
add_header (s)
|
||||
end
|
||||
|
||||
put_cookie_with_expiration_date (key, value: READABLE_STRING_8; expiration: DATE_TIME; path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN)
|
||||
-- Set a cookie on the client's machine
|
||||
-- with key 'key' and value 'value'.
|
||||
require
|
||||
make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty)
|
||||
do
|
||||
put_cookie (key, value, date_to_rfc1123_http_date_format (expiration), path, domain, secure, http_only)
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
header_named_value (a_name: READABLE_STRING_8): detachable STRING_8
|
||||
-- Has header item for `n'?
|
||||
require
|
||||
has_header: has_header_named (a_name)
|
||||
local
|
||||
c: like headers.new_cursor
|
||||
n: INTEGER
|
||||
l_line: READABLE_STRING_8
|
||||
do
|
||||
from
|
||||
n := a_name.count
|
||||
c := headers.new_cursor
|
||||
until
|
||||
c.after or Result /= Void
|
||||
loop
|
||||
l_line := c.item
|
||||
if l_line.starts_with (a_name) then
|
||||
if l_line.valid_index (n + 1) then
|
||||
if l_line [n + 1] = ':' then
|
||||
Result := l_line.substring (n + 2, l_line.count)
|
||||
Result.left_adjust
|
||||
Result.right_adjust
|
||||
end
|
||||
end
|
||||
end
|
||||
c.forth
|
||||
end
|
||||
end
|
||||
|
||||
has_header_named (a_name: READABLE_STRING_8): BOOLEAN
|
||||
-- Has header item for `n'?
|
||||
local
|
||||
c: like headers.new_cursor
|
||||
n: INTEGER
|
||||
l_line: READABLE_STRING_8
|
||||
do
|
||||
from
|
||||
n := a_name.count
|
||||
c := headers.new_cursor
|
||||
until
|
||||
c.after or Result
|
||||
loop
|
||||
l_line := c.item
|
||||
if l_line.starts_with (a_name) then
|
||||
if l_line.valid_index (n + 1) then
|
||||
Result := l_line [n + 1] = ':'
|
||||
end
|
||||
end
|
||||
c.forth
|
||||
end
|
||||
end
|
||||
|
||||
has_content_length: BOOLEAN
|
||||
-- Has header "Content-Length"
|
||||
do
|
||||
Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_length)
|
||||
end
|
||||
|
||||
has_content_type: BOOLEAN
|
||||
-- Has header "Content-Type"
|
||||
do
|
||||
Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_type)
|
||||
end
|
||||
|
||||
has_transfer_encoding_chunked: BOOLEAN
|
||||
-- Has "Transfer-Encoding: chunked" header
|
||||
do
|
||||
if has_header_named ({HTTP_HEADER_NAMES}.header_transfer_encoding) then
|
||||
Result := attached header_named_value ({HTTP_HEADER_NAMES}.header_transfer_encoding) as v and then v.same_string (str_chunked)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
feature {NONE} -- Implementation: Header
|
||||
|
||||
force_header_by_name (n: detachable READABLE_STRING_8; h: READABLE_STRING_8)
|
||||
-- Add header `h' or replace existing header of same header name `n'
|
||||
require
|
||||
h_has_name_n: (n /= Void and attached header_name_colon (h) as hn) implies n.same_string (hn)
|
||||
local
|
||||
l_headers: like headers
|
||||
do
|
||||
if n /= Void then
|
||||
from
|
||||
l_headers := headers
|
||||
l_headers.start
|
||||
until
|
||||
l_headers.after or l_headers.item.starts_with (n)
|
||||
loop
|
||||
l_headers.forth
|
||||
end
|
||||
if not l_headers.after then
|
||||
l_headers.replace (h)
|
||||
else
|
||||
add_header (h)
|
||||
end
|
||||
else
|
||||
add_header (h)
|
||||
end
|
||||
end
|
||||
|
||||
header_name_colon (h: READABLE_STRING_8): detachable STRING_8
|
||||
-- If any, header's name with colon
|
||||
--| ex: for "Foo-bar: something", this will return "Foo-bar:"
|
||||
local
|
||||
s: detachable STRING_8
|
||||
i,n: INTEGER
|
||||
c: CHARACTER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := h.count
|
||||
create s.make (10)
|
||||
until
|
||||
i > n or c = ':' or s = Void
|
||||
loop
|
||||
c := h[i]
|
||||
inspect c
|
||||
when ':' then
|
||||
s.extend (c)
|
||||
when '-', 'a' .. 'z', 'A' .. 'Z' then
|
||||
s.extend (c)
|
||||
else
|
||||
s := Void
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
Result := s
|
||||
end
|
||||
|
||||
header_name_value (h: READABLE_STRING_8): detachable TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]
|
||||
-- If any, header's [name,value]
|
||||
--| ex: for "Foo-bar: something", this will return ["Foo-bar", "something"]
|
||||
local
|
||||
s: detachable STRING_8
|
||||
i,n: INTEGER
|
||||
c: CHARACTER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := h.count
|
||||
create s.make (10)
|
||||
until
|
||||
i > n or c = ':' or s = Void
|
||||
loop
|
||||
c := h[i]
|
||||
inspect c
|
||||
when ':' then
|
||||
when '-', 'a' .. 'z', 'A' .. 'Z' then
|
||||
s.extend (c)
|
||||
else
|
||||
s := Void
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
if s /= Void then
|
||||
Result := [s, h.substring (i, n)]
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
append_line_to (s: READABLE_STRING_8; h: STRING_8)
|
||||
do
|
||||
h.append_string (s)
|
||||
append_end_of_line_to (h)
|
||||
end
|
||||
|
||||
append_end_of_line_to (h: STRING_8)
|
||||
do
|
||||
h.append_character ('%R')
|
||||
h.append_character ('%N')
|
||||
end
|
||||
|
||||
date_to_rfc1123_http_date_format (dt: DATE_TIME): STRING_8
|
||||
-- String representation of `dt' using the RFC 1123
|
||||
do
|
||||
Result := dt.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT"
|
||||
end
|
||||
|
||||
feature {NONE} -- Constants
|
||||
|
||||
str_binary: STRING = "binary"
|
||||
str_chunked: STRING = "chunked"
|
||||
|
||||
colon_space: STRING = ": "
|
||||
semi_colon_space: STRING = "; "
|
||||
|
||||
note
|
||||
copyright: "2011-2012, 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
|
||||
260
library/network/protocol/http/src/http_header_names.e
Normal file
260
library/network/protocol/http/src/http_header_names.e
Normal file
@@ -0,0 +1,260 @@
|
||||
note
|
||||
description: "[
|
||||
See http://en.wikipedia.org/wiki/List_of_HTTP_headers
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_HEADER_NAMES
|
||||
|
||||
feature -- Request header name
|
||||
|
||||
header_accept: STRING = "Accept"
|
||||
-- Content-Types that are acceptable
|
||||
--| Example: Accept: text/plain
|
||||
|
||||
header_accept_charset: STRING = "Accept-Charset"
|
||||
-- Character sets that are acceptable
|
||||
--| Example: Accept-Charset: utf-8
|
||||
|
||||
header_accept_encoding: STRING = "Accept-Encoding"
|
||||
-- Acceptable encodings. See HTTP compression.
|
||||
--| Example: Accept-Encoding: <compress | gzip | deflate | sdch | identity>
|
||||
|
||||
header_accept_language: STRING = "Accept-Language"
|
||||
-- Acceptable languages for response
|
||||
--| Example: Accept-Language: en-US
|
||||
|
||||
header_authorization: STRING = "Authorization"
|
||||
-- Authentication credentials for HTTP authentication
|
||||
--| Example: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
|
||||
|
||||
header_cookie: STRING = "Cookie"
|
||||
-- an HTTP cookie previously sent by the server with Set-Cookie (below)
|
||||
--| Example: Cookie: $Version=1; Skin=new;
|
||||
|
||||
header_expect: STRING = "Expect"
|
||||
-- Indicates that particular server behaviors are required by the client
|
||||
--| Example: Expect: 100-continue
|
||||
|
||||
header_from: STRING = "From"
|
||||
-- The email address of the user making the request
|
||||
--| Example: From: user@example.com
|
||||
|
||||
header_host: STRING = "Host"
|
||||
-- The domain name of the server (for virtual hosting), mandatory since HTTP/1.1
|
||||
--| Example: Host: en.wikipedia.org
|
||||
|
||||
header_if_match: STRING = "If-Match"
|
||||
-- Only perform the action if the client supplied entity matches the same entity on the server. This is mainly for methods like PUT to only update a resource if it has not been modified since the user last updated it.
|
||||
--| Example: If-Match: "737060cd8c284d8af7ad3082f209582d"
|
||||
|
||||
header_if_modified_since: STRING = "If-Modified-Since"
|
||||
-- Allows a 304 Not Modified to be returned if content is unchanged
|
||||
--| Example: If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
|
||||
|
||||
header_if_none_match: STRING = "If-None-Match"
|
||||
-- Allows a 304 Not Modified to be returned if content is unchanged, see HTTP ETag
|
||||
--| Example: If-None-Match: "737060cd8c284d8af7ad3082f209582d"
|
||||
|
||||
header_if_range: STRING = "If-Range"
|
||||
-- If the entity is unchanged, send me the part(s) that I am missing; otherwise, send me the entire new entity
|
||||
--| Example: If-Range: "737060cd8c284d8af7ad3082f209582d"
|
||||
|
||||
header_if_unmodified_since: STRING = "If-Unmodified-Since"
|
||||
-- Only send the response if the entity has not been modified since a specific time.
|
||||
--| Example: If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT
|
||||
|
||||
header_max_forwards: STRING = "Max-Forwards"
|
||||
-- Limit the number of times the message can be forwarded through proxies or gateways.
|
||||
--| Example: Max-Forwards: 10
|
||||
|
||||
header_proxy_authorization: STRING = "Proxy-Authorization"
|
||||
-- Authorization credentials for connecting to a proxy.
|
||||
--| Example: Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
|
||||
|
||||
header_range: STRING = "Range"
|
||||
-- Request only part of an entity. Bytes are numbered from 0.
|
||||
--| Example: Range: bytes=500-999
|
||||
|
||||
header_referer: STRING = "Referer[sic]"
|
||||
-- This is the address of the previous web page from which a link to the currently requested page was followed. (The word “referrer” is misspelled in the RFC as well as in most implementations.)
|
||||
--| Example: Referer: http://en.wikipedia.org/wiki/Main_Page
|
||||
|
||||
header_te: STRING = "TE"
|
||||
-- The transfer encodings the user agent is willing to accept: the same values as for the response header Transfer-Encoding can be used, plus the "trailers" value (related to the "chunked" transfer method) to notify the server it accepts to receive additional headers (the trailers) after the last, zero-sized, chunk.
|
||||
--| Example: TE: trailers, deflate
|
||||
|
||||
header_upgrade: STRING = "Upgrade"
|
||||
-- Ask the server to upgrade to another protocol.
|
||||
--| Example: Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
|
||||
|
||||
header_user_agent: STRING = "User-Agent"
|
||||
-- The user agent string of the user agent
|
||||
--| Example: User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
|
||||
|
||||
feature -- Response header name
|
||||
|
||||
header_accept_ranges: STRING = "Accept-Ranges"
|
||||
-- "Accept-Ranges" What partial content range types this server supports
|
||||
--| Example: Accept-Ranges: bytes
|
||||
|
||||
header_age: STRING = "Age"
|
||||
-- The age the object has been in a proxy cache in seconds
|
||||
--| Example: Age: 12
|
||||
|
||||
header_allow: STRING = "Allow"
|
||||
-- Valid actions for a specified resource. To be used for a 405 Method not allowed
|
||||
--| Example: Allow: GET, HEAD
|
||||
|
||||
header_content_encoding: STRING = "Content-Encoding"
|
||||
-- The type of encoding used on the data. See HTTP compression.
|
||||
--| Example: Content-Encoding: gzip
|
||||
|
||||
header_content_language: STRING = "Content-Language"
|
||||
-- The language the content is in
|
||||
--| Example: Content-Language: da
|
||||
|
||||
header_content_location: STRING = "Content-Location"
|
||||
-- An alternate location for the returned data
|
||||
--| Example: Content-Location: /index.htm
|
||||
|
||||
header_content_disposition: STRING = "Content-Disposition"
|
||||
-- An opportunity to raise a "File Download" dialogue box for a known MIME type
|
||||
--| Example: Content-Disposition: attachment; filename=fname.ext
|
||||
|
||||
header_content_range: STRING = "Content-Range"
|
||||
-- Where in a full body message this partial message belongs
|
||||
--| Example: Content-Range: bytes 21010-47021/47022
|
||||
|
||||
header_etag: STRING = "ETag"
|
||||
-- An identifier for a specific version of a resource, often a message digest
|
||||
--| Example: ETag: "737060cd8c284d8af7ad3082f209582d"
|
||||
|
||||
header_expires: STRING = "Expires"
|
||||
-- Gives the date/time after which the response is considered stale
|
||||
--| Example: Expires: Thu, 01 Dec 1994 16:00:00 GMT
|
||||
|
||||
header_last_modified: STRING = "Last-Modified"
|
||||
-- The last modified date for the requested object, in RFC 2822 format
|
||||
--| Example: Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT
|
||||
|
||||
header_link: STRING = "Link"
|
||||
-- Used to express a typed relationship with another resource, where the relation type is defined by RFC 5988
|
||||
--| Example: Link: </feed>; rel="alternate"
|
||||
|
||||
header_location: STRING = "Location"
|
||||
-- Used in redirection, or when a new resource has been created.
|
||||
--| Example: Location: http://www.w3.org/pub/WWW/People.html
|
||||
|
||||
header_p3p: STRING = "P3P"
|
||||
-- This header is supposed to set P3P policy, in the form of P3P:CP="your_compact_policy". However, P3P did not take off,[5] most browsers have never fully implemented it, a lot of websites set this header with fake policy text, that was enough to fool browsers the existence of P3P policy and grant permissions for third party cookies.
|
||||
--| Example: P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info."
|
||||
|
||||
|
||||
header_proxy_authenticate: STRING = "Proxy-Authenticate"
|
||||
-- Request authentication to access the proxy.
|
||||
--| Example: Proxy-Authenticate: Basic
|
||||
|
||||
header_refresh: STRING = "Refresh"
|
||||
-- Used in redirection, or when a new resource has been created. This refresh redirects after 5 seconds. This is a proprietary, non-standard header extension introduced by Netscape and supported by most web browsers.
|
||||
--| Example: Refresh: 5; url=http://www.w3.org/pub/WWW/People.html
|
||||
|
||||
header_retry_after: STRING = "Retry-After"
|
||||
-- If an entity is temporarily unavailable, this instructs the client to try again after a specified period of time.
|
||||
--| Example: Retry-After: 120
|
||||
|
||||
header_server: STRING = "Server"
|
||||
-- A name for the server
|
||||
--| Example: Server: Apache/1.3.27 (Unix) (Red-Hat/Linux)
|
||||
|
||||
header_set_cookie: STRING = "Set-Cookie"
|
||||
-- an HTTP cookie
|
||||
--| Example: Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
|
||||
|
||||
header_strict_transport_security: STRING = "Strict-Transport-Security"
|
||||
-- A HSTS Policy informing the HTTP client how long to cache the HTTPS only policy and whether this applies to subdomains.
|
||||
--| Example: Strict-Transport-Security: max-age=16070400; includeSubDomains
|
||||
|
||||
header_trailer: STRING = "Trailer"
|
||||
-- The Trailer general field value indicates that the given set of header fields is present in the trailer of a message encoded with chunked transfer-coding.
|
||||
--| Example: Trailer: Max-Forwards
|
||||
|
||||
header_transfer_encoding: STRING = "Transfer-Encoding"
|
||||
-- The form of encoding used to safely transfer the entity to the user. Currently defined methods are: chunked, compress, deflate, gzip, identity.
|
||||
--| Example: Transfer-Encoding: chunked
|
||||
|
||||
header_vary: STRING = "Vary"
|
||||
-- Tells downstream proxies how to match future request headers to decide whether the cached response
|
||||
-- can be used rather than requesting a fresh one from the origin server.
|
||||
--| Example: Vary: *
|
||||
|
||||
header_www_authenticate: STRING = "WWW-Authenticate"
|
||||
-- Indicates the authentication scheme that should be used to access the requested entity.
|
||||
--| Example: WWW-Authenticate: Basic
|
||||
|
||||
feature -- Request or Response header name
|
||||
|
||||
header_cache_control: STRING = "Cache-Control"
|
||||
-- Request: Used to specify directives that MUST be obeyed by all caching mechanisms along the request/response chain
|
||||
-- Response: Tells all caching mechanisms from server to client whether they may cache this object. It is measured in seconds
|
||||
--| Request example: Cache-Control: no-cache
|
||||
--| Response example: Cache-Control: max-age=3600
|
||||
|
||||
header_connection: STRING = "Connection"
|
||||
-- Request: What type of connection the user-agent would prefer
|
||||
-- Response: Options that are desired for the connection[4]
|
||||
--| Example: Connection: close
|
||||
|
||||
header_content_length: STRING = "Content-Length"
|
||||
-- Request: The length of the request body in octets (8-bit bytes)
|
||||
-- Response: The length of the response body in octets (8-bit bytes)
|
||||
--| Example: Content-Length: 348
|
||||
|
||||
header_content_md5: STRING = "Content-MD5"
|
||||
-- Request: A Base64-encoded binary MD5 sum of the content of the request body
|
||||
-- Response: A Base64-encoded binary MD5 sum of the content of the response
|
||||
--| Example: Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==
|
||||
|
||||
header_content_type: STRING = "Content-Type"
|
||||
-- Request: The mime type of the body of the request (used with POST and PUT requests)
|
||||
-- Response : The mime type of this content
|
||||
--| Request example: Content-Type: application/x-www-form-urlencoded
|
||||
--| Response example: Content-Type: text/html; charset=utf-8
|
||||
|
||||
header_date: STRING = "Date"
|
||||
-- The date and time that the message was sent
|
||||
--| Example: Date: Tue, 15 Nov 1994 08:12:31 GMT
|
||||
|
||||
header_pragma: STRING = "Pragma"
|
||||
-- Implementation-specific headers that may have various effects anywhere along the request-response chain.
|
||||
--| Example: Pragma: no-cache
|
||||
|
||||
header_status: STRING = "Status"
|
||||
-- CGI program can use this to return the HTTP status code to the client.
|
||||
|
||||
header_via: STRING = "Via"
|
||||
-- Request: Informs the server of proxies through which the request was sent.
|
||||
-- Response: Informs the client of proxies through which the response was sent.
|
||||
--| Example: Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
|
||||
|
||||
header_warning: STRING = "Warning"
|
||||
-- A general warning about possible problems with the entity body.
|
||||
--| Example: Warning: 199 Miscellaneous warning
|
||||
|
||||
feature -- MIME related
|
||||
|
||||
header_content_transfer_encoding: STRING = "Content-Transfer-Encoding"
|
||||
|
||||
note
|
||||
copyright: "2011-2012, 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
|
||||
346
library/network/protocol/http/src/http_media_type.e
Normal file
346
library/network/protocol/http/src/http_media_type.e
Normal file
@@ -0,0 +1,346 @@
|
||||
note
|
||||
description: "[
|
||||
This class is to represent a media type
|
||||
|
||||
the Internet Media Type [9] of the attached entity if the type
|
||||
was provided via a "Content-type" field in the wgi_request header,
|
||||
or if the server can determine it in the absence of a supplied
|
||||
"Content-type" field. The syntax is the same as for the HTTP
|
||||
"Content-Type" header field.
|
||||
|
||||
CONTENT_TYPE = "" | media-type
|
||||
media-type = type "/" subtype *( ";" parameter)
|
||||
type = token
|
||||
subtype = token
|
||||
parameter = attribute "=" value
|
||||
attribute = token
|
||||
value = token | quoted-string
|
||||
|
||||
The type, subtype, and parameter attribute names are not
|
||||
case-sensitive. Parameter values MAY be case sensitive. Media
|
||||
types and their use in HTTP are described in section 3.7 of
|
||||
the HTTP/1.1 specification [8].
|
||||
|
||||
Example:
|
||||
|
||||
application/x-www-form-urlencoded
|
||||
application/x-www-form-urlencoded; charset=UTF8
|
||||
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_MEDIA_TYPE
|
||||
|
||||
inherit
|
||||
DEBUG_OUTPUT
|
||||
|
||||
create
|
||||
make,
|
||||
make_from_string
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_type, a_subtype: READABLE_STRING_8)
|
||||
-- Create Current based on `a_type/a_subtype'
|
||||
require
|
||||
not a_type.is_empty
|
||||
not a_subtype.is_empty
|
||||
do
|
||||
type := a_type
|
||||
subtype := a_subtype
|
||||
ensure
|
||||
has_no_error: not has_error
|
||||
end
|
||||
|
||||
make_from_string (s: READABLE_STRING_8)
|
||||
-- Create Current from `s'
|
||||
-- if `s' does not respect the expected syntax, has_error is True
|
||||
local
|
||||
t: STRING_8
|
||||
i,n: INTEGER
|
||||
p: INTEGER
|
||||
do
|
||||
-- Ignore starting space (should not be any)
|
||||
from
|
||||
i := 1
|
||||
n := s.count
|
||||
until
|
||||
i > n or not s[i].is_space
|
||||
loop
|
||||
i := i + 1
|
||||
end
|
||||
if i < n then
|
||||
-- Look for semi colon as parameter separation
|
||||
p := s.index_of (';', i)
|
||||
if p > 0 then
|
||||
t := s.substring (i, p - 1)
|
||||
i := p + 1
|
||||
p := s.index_of (';', i)
|
||||
if p = 0 then
|
||||
add_parameter_from_string (s, i, n)
|
||||
i := n
|
||||
else
|
||||
add_parameter_from_string (s, i, p - 1)
|
||||
i := p + 1
|
||||
end
|
||||
else
|
||||
t := s.substring (i, n)
|
||||
end
|
||||
-- Remove eventual trailing space
|
||||
t.right_adjust
|
||||
|
||||
-- Extract type and subtype
|
||||
p := t.index_of ('/', 1)
|
||||
if p = 0 then
|
||||
has_error := True
|
||||
type := t
|
||||
subtype := ""
|
||||
else
|
||||
subtype := t.substring (p + 1, t.count)
|
||||
type := t
|
||||
t.keep_head (p - 1)
|
||||
end
|
||||
else
|
||||
has_error := True
|
||||
type := ""
|
||||
subtype := type
|
||||
end
|
||||
ensure
|
||||
not has_error implies (create {HTTP_CONTENT_TYPE}.make_from_string (string)).same_string (string)
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
has_error: BOOLEAN
|
||||
-- Current has error?
|
||||
--| Mainly in relation with `make_from_string'
|
||||
|
||||
feature -- Access
|
||||
|
||||
type: READABLE_STRING_8
|
||||
-- Main type
|
||||
|
||||
subtype: READABLE_STRING_8
|
||||
-- Sub type
|
||||
|
||||
has_parameter (a_name: READABLE_STRING_8): BOOLEAN
|
||||
-- Has Current a parameter?
|
||||
do
|
||||
if attached parameters as plst then
|
||||
Result := plst.has (a_name)
|
||||
end
|
||||
end
|
||||
|
||||
parameter (a_name: READABLE_STRING_8): detachable READABLE_STRING_8
|
||||
-- Value for eventual parameter named `a_name'.
|
||||
do
|
||||
if attached parameters as plst then
|
||||
Result := plst.item (a_name)
|
||||
end
|
||||
end
|
||||
|
||||
parameters: detachable HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
|
||||
-- Parameters
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
string: READABLE_STRING_8
|
||||
-- String representation of type/subtype; attribute=value
|
||||
local
|
||||
res: like internal_string
|
||||
do
|
||||
res := internal_string
|
||||
if res = Void then
|
||||
create res.make_from_string (simple_type)
|
||||
if attached parameters as plst then
|
||||
across
|
||||
plst as p
|
||||
loop
|
||||
res.append_character (';')
|
||||
res.append_character (' ')
|
||||
res.append (p.key)
|
||||
res.append_character ('=')
|
||||
res.append_character ('%"')
|
||||
res.append (p.item)
|
||||
res.append_character ('%"')
|
||||
end
|
||||
end
|
||||
internal_string := res
|
||||
end
|
||||
Result := res
|
||||
end
|
||||
|
||||
simple_type: READABLE_STRING_8
|
||||
-- String representation of type/subtype
|
||||
local
|
||||
res: like internal_simple_type
|
||||
s: like subtype
|
||||
do
|
||||
res := internal_simple_type
|
||||
if res = Void then
|
||||
create res.make_from_string (type)
|
||||
s := subtype
|
||||
if not s.is_empty then
|
||||
check has_error: has_error end
|
||||
-- Just in case not is_valid, we keep in `type' the original string
|
||||
res.append_character ('/')
|
||||
res.append (s)
|
||||
end
|
||||
internal_simple_type := res
|
||||
end
|
||||
Result := res
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
same_as (other: HTTP_CONTENT_TYPE): BOOLEAN
|
||||
-- Current has same type/subtype and parameters as `other'?
|
||||
local
|
||||
plst,oplst: like parameters
|
||||
do
|
||||
Result := other.type.same_string (other.type) and then
|
||||
other.subtype.same_string (other.subtype)
|
||||
if Result then
|
||||
plst := parameters
|
||||
oplst := other.parameters
|
||||
if plst = oplst then
|
||||
elseif plst = Void then
|
||||
Result := False -- since oplst /= Void
|
||||
elseif oplst /= Void and then plst.count = oplst.count then
|
||||
across
|
||||
plst as p
|
||||
until
|
||||
not Result
|
||||
loop
|
||||
Result := attached oplst.item (p.key) as op_value and then p.item.same_string (op_value)
|
||||
end
|
||||
else
|
||||
Result := False
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
same_simple_type (s: READABLE_STRING_8): BOOLEAN
|
||||
-- Current has same type/subtype string representation as `s'?
|
||||
do
|
||||
Result := simple_type.same_string (s)
|
||||
end
|
||||
|
||||
same_string (s: READABLE_STRING_8): BOOLEAN
|
||||
-- Current has same string representation as `s'?
|
||||
do
|
||||
Result := string.same_string (s)
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
add_parameter (a_name: READABLE_STRING_8; a_value: READABLE_STRING_8)
|
||||
-- Set parameter for `a_name' to `a_value'
|
||||
local
|
||||
plst: like parameters
|
||||
do
|
||||
plst := parameters
|
||||
if plst = Void then
|
||||
create plst.make (1)
|
||||
parameters := plst
|
||||
end
|
||||
plst.force (a_value, a_name)
|
||||
internal_string := Void
|
||||
end
|
||||
|
||||
remove_parameter (a_name: READABLE_STRING_8)
|
||||
-- Remove parameter named `a_name'
|
||||
do
|
||||
if attached parameters as plst then
|
||||
plst.prune (a_name)
|
||||
if plst.is_empty then
|
||||
parameters := Void
|
||||
end
|
||||
end
|
||||
internal_string := Void
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
add_parameter_from_string (s: READABLE_STRING_8; start_index, end_index: INTEGER)
|
||||
-- Add parameter from string " attribute=value "
|
||||
local
|
||||
pn,pv: STRING_8
|
||||
i: INTEGER
|
||||
p: INTEGER
|
||||
err: BOOLEAN
|
||||
do
|
||||
-- Skip spaces
|
||||
from
|
||||
i := start_index
|
||||
until
|
||||
i > end_index or not s[i].is_space
|
||||
loop
|
||||
i := i + 1
|
||||
end
|
||||
if i < end_index then
|
||||
p := s.index_of ('=', i)
|
||||
if p > 0 and p < end_index then
|
||||
pn := s.substring (i, p - 1)
|
||||
pv := s.substring (p + 1, end_index)
|
||||
pv.right_adjust
|
||||
if pv.count > 0 and pv [1] = '%"' then
|
||||
if pv [pv.count] = '%"' then
|
||||
pv := pv.substring (2, pv.count - 1)
|
||||
else
|
||||
err := True
|
||||
-- missing closing double quote.
|
||||
end
|
||||
end
|
||||
if not err then
|
||||
add_parameter (pn, pv)
|
||||
end
|
||||
else
|
||||
-- expecting: attribute "=" value
|
||||
err := True
|
||||
end
|
||||
end
|
||||
has_error := has_error or err
|
||||
end
|
||||
|
||||
feature {NONE} -- Internal
|
||||
|
||||
internal_string: detachable STRING_8
|
||||
|
||||
internal_simple_type: detachable STRING_8
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
if type /= Void and subtype /= Void then
|
||||
Result := type + "/" + subtype
|
||||
if attached parameters as plst then
|
||||
across
|
||||
plst as p
|
||||
loop
|
||||
Result.append ("; " + p.key + "=" + "%"" + p.item + "%"")
|
||||
end
|
||||
end
|
||||
else
|
||||
Result := ""
|
||||
end
|
||||
end
|
||||
|
||||
invariant
|
||||
type_and_subtype_not_empty: not has_error implies not type.is_empty and not subtype.is_empty
|
||||
|
||||
note
|
||||
copyright: "2011-2012, 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
|
||||
185
library/network/protocol/http/src/http_mime_types.e
Normal file
185
library/network/protocol/http/src/http_mime_types.e
Normal file
@@ -0,0 +1,185 @@
|
||||
note
|
||||
description: "[
|
||||
Various common MIME types
|
||||
|
||||
See also for longer list and description
|
||||
http://www.iana.org/assignments/media-types/index.html
|
||||
http://www.webmaster-toolkit.com/mime-types.shtml
|
||||
|
||||
Please suggest common/popular MIME type which might be missing here
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_MIME_TYPES
|
||||
|
||||
feature -- Content type : application
|
||||
|
||||
application_atom_xml: STRING = "application/atom+xml"
|
||||
-- atom application content-type header
|
||||
|
||||
application_force_download: STRING = "application/force-download"
|
||||
|
||||
application_javascript: STRING = "application/javascript"
|
||||
-- JavaScript application content-type header
|
||||
|
||||
application_json: STRING = "application/json"
|
||||
-- JSON application content-type header
|
||||
|
||||
application_octet_stream: STRING = "application/octet-stream"
|
||||
-- Octet stream content-type header
|
||||
|
||||
application_pdf: STRING = "application/pdf"
|
||||
-- pdf application content-type header
|
||||
|
||||
application_postscript: STRING = "application/postscript"
|
||||
-- postscript application content-type header
|
||||
|
||||
application_rss_xml: STRING = "application/rss+xml"
|
||||
-- rss application content-type header
|
||||
|
||||
application_rtf: STRING = "application/rtf"
|
||||
-- RTF application content-type header
|
||||
|
||||
application_xml: STRING = "application/xml"
|
||||
-- xml application content-type header
|
||||
|
||||
application_x_shockwave_flash: STRING = "application/x-shockwave-flash"
|
||||
|
||||
application_x_compressed: STRING = "application/x-compressed"
|
||||
|
||||
application_x_gzip: STRING = "application/x-gzip"
|
||||
|
||||
application_zip: STRING = "application/zip"
|
||||
|
||||
application_x_bzip: STRING = "application/x-bzip"
|
||||
|
||||
application_x_bzip2: STRING = "application/x-bzip2"
|
||||
|
||||
application_x_tar: STRING = "application/x-tar"
|
||||
|
||||
application_x_www_form_encoded: STRING = "application/x-www-form-urlencoded"
|
||||
-- Starting chars of form url-encoded data content-type header
|
||||
|
||||
feature -- Content type : audio
|
||||
|
||||
audio_mpeg3: STRING = "audio/mpeg3"
|
||||
|
||||
audio_mpeg: STRING = "audio/mpeg"
|
||||
|
||||
audio_wav: STRING = "audio/wav"
|
||||
|
||||
feature -- Content type : image
|
||||
|
||||
image_bmp: STRING = "image/bmp"
|
||||
-- BMP image content-type header
|
||||
|
||||
image_gif: STRING = "image/gif"
|
||||
-- GIF image content-type header
|
||||
|
||||
image_jpeg: STRING = "image/jpeg"
|
||||
-- JPEG image content-type header
|
||||
|
||||
image_jpg: STRING = "image/jpg"
|
||||
-- JPEG image content-type header
|
||||
|
||||
image_png: STRING = "image/png"
|
||||
-- PNG image content-type header
|
||||
|
||||
image_svg_xml: STRING = "image/svg+xml"
|
||||
-- SVG+XML image content-type header
|
||||
|
||||
image_tiff: STRING = "image/tiff"
|
||||
-- TIFF image content-type header
|
||||
|
||||
image_x_ico: STRING = "image/x-ico"
|
||||
-- ICO image content-type header
|
||||
|
||||
feature -- Content type : message
|
||||
|
||||
message_http: STRING = "message/http"
|
||||
-- http message content-type header
|
||||
|
||||
message_s_http: STRING = "message/s-http"
|
||||
-- s-http message content-type
|
||||
|
||||
message_partial: STRING = "message/partial"
|
||||
-- partial message content-type
|
||||
|
||||
message_sip: STRING = "message/sip"
|
||||
-- sip message content-type
|
||||
|
||||
feature -- Content type : model
|
||||
|
||||
model_vrml: STRING = "model/vrml"
|
||||
|
||||
feature -- Content type : multipart
|
||||
|
||||
multipart_mixed: STRING = "multipart/mixed"
|
||||
|
||||
multipart_alternative: STRING = "multipart/alternative"
|
||||
|
||||
multipart_related: STRING = "multipart/related"
|
||||
|
||||
multipart_form_data: STRING = "multipart/form-data"
|
||||
|
||||
multipart_signed: STRING = "multipart/signed"
|
||||
|
||||
multipart_encrypted: STRING = "multipart/encrypted"
|
||||
|
||||
multipart_x_gzip: STRING = "multipart/x-gzip"
|
||||
|
||||
feature -- Content type : text
|
||||
|
||||
text_css: STRING = "text/css"
|
||||
-- CSS text content-type header
|
||||
|
||||
text_csv: STRING = "text/csv"
|
||||
-- CSV text content-type header
|
||||
|
||||
text_html: STRING = "text/html"
|
||||
-- HTML text content-type header
|
||||
|
||||
text_javascript: STRING = "text/javascript"
|
||||
-- Javascript text content-type header
|
||||
-- OBSOLETE
|
||||
|
||||
text_json: STRING = "text/json"
|
||||
-- JSON text content-type header
|
||||
|
||||
text_plain: STRING = "text/plain"
|
||||
-- Plain text content-type header
|
||||
|
||||
text_rtf: STRING = "text/rtf"
|
||||
-- rtf content-type header
|
||||
|
||||
text_tab_separated_values: STRING = "text/tab-separated-values"
|
||||
-- TSV text content-type header
|
||||
|
||||
text_xml: STRING = "text/xml"
|
||||
-- XML text content-type header
|
||||
|
||||
text_vcard: STRING = "text/vcard"
|
||||
-- vcard text content-type header
|
||||
|
||||
feature -- Content type : video
|
||||
|
||||
video_avi: STRING = "video/avi"
|
||||
|
||||
video_quicktime: STRING = "video/quicktime"
|
||||
|
||||
video_x_motion_jpeg: STRING = "video/x-motion-jpeg"
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2011, 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
|
||||
@@ -0,0 +1,94 @@
|
||||
note
|
||||
description: "Summary description for {HTTP_REQUEST_METHOD_CONSTANTS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_REQUEST_METHOD_CONSTANTS
|
||||
|
||||
inherit
|
||||
HTTP_REQUEST_METHODS
|
||||
|
||||
feature -- Id
|
||||
|
||||
head: INTEGER = 0x1
|
||||
|
||||
get: INTEGER = 0x2
|
||||
|
||||
trace: INTEGER = 0x4
|
||||
|
||||
options: INTEGER = 0x8
|
||||
|
||||
post: INTEGER = 0x10
|
||||
|
||||
put: INTEGER = 0x20
|
||||
|
||||
delete: INTEGER = 0x40
|
||||
|
||||
connect: INTEGER = 0x80
|
||||
|
||||
patch: INTEGER = 0x100
|
||||
|
||||
feature -- Name
|
||||
|
||||
method_empty: STRING = ""
|
||||
|
||||
feature -- Query
|
||||
|
||||
method_id (a_id: STRING): INTEGER
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
s := a_id.as_lower
|
||||
if s.same_string (method_get) then
|
||||
Result := get
|
||||
elseif s.same_string (method_post) then
|
||||
Result := post
|
||||
elseif s.same_string (method_put) then
|
||||
Result := put
|
||||
elseif s.same_string (method_patch) then
|
||||
Result := patch
|
||||
elseif s.same_string (method_delete) then
|
||||
Result := delete
|
||||
elseif s.same_string (method_head) then
|
||||
Result := head
|
||||
elseif s.same_string (method_trace) then
|
||||
Result := trace
|
||||
elseif s.same_string (method_options) then
|
||||
Result := options
|
||||
elseif s.same_string (method_connect) then
|
||||
Result := connect
|
||||
end
|
||||
end
|
||||
|
||||
method_name (a_id: INTEGER): STRING
|
||||
do
|
||||
inspect a_id
|
||||
when head then Result := method_head
|
||||
when get then Result := method_get
|
||||
when trace then Result := method_trace
|
||||
when options then Result := method_options
|
||||
when post then Result := method_post
|
||||
when put then Result := method_put
|
||||
when patch then Result := method_patch
|
||||
when delete then Result := method_delete
|
||||
when connect then Result := method_connect
|
||||
else
|
||||
Result := method_empty
|
||||
end
|
||||
ensure
|
||||
result_is_upper_case: Result /= Void and then Result.as_upper ~ Result
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, 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
|
||||
104
library/network/protocol/http/src/http_request_methods.e
Normal file
104
library/network/protocol/http/src/http_request_methods.e
Normal file
@@ -0,0 +1,104 @@
|
||||
note
|
||||
description: "[
|
||||
|
||||
Safe method: HEAD, GET, TRACE, OPTIONS
|
||||
Intended only for information retrieval, should not change state of server
|
||||
|
||||
|
||||
See http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_REQUEST_METHODS
|
||||
|
||||
feature -- Query
|
||||
|
||||
method (m: READABLE_STRING_8): READABLE_STRING_8
|
||||
-- Return the associated constant object if any
|
||||
-- otherwise the uppercased version of `m'
|
||||
do
|
||||
if m.is_case_insensitive_equal (method_get) then
|
||||
Result := method_get
|
||||
elseif m.is_case_insensitive_equal (method_post) then
|
||||
Result := method_post
|
||||
elseif m.is_case_insensitive_equal (method_head) then
|
||||
Result := method_head
|
||||
elseif m.is_case_insensitive_equal (method_trace) then
|
||||
Result := method_trace
|
||||
elseif m.is_case_insensitive_equal (method_options) then
|
||||
Result := method_options
|
||||
elseif m.is_case_insensitive_equal (method_put) then
|
||||
Result := method_put
|
||||
elseif m.is_case_insensitive_equal (method_delete) then
|
||||
Result := method_delete
|
||||
elseif m.is_case_insensitive_equal (method_connect) then
|
||||
Result := method_connect
|
||||
elseif m.is_case_insensitive_equal (method_patch) then
|
||||
Result := method_patch
|
||||
else
|
||||
Result := m.as_upper
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Safe Methods
|
||||
|
||||
method_head: STRING = "HEAD"
|
||||
-- Asks for the response identical to the one that would correspond
|
||||
-- to a GET request, but without the response body.
|
||||
-- This is useful for retrieving meta-information written in response headers,
|
||||
-- without having to transport the entire content.
|
||||
|
||||
method_get: STRING = "GET"
|
||||
-- Requests a representation of the specified resource.
|
||||
-- Requests using GET (and a few other HTTP methods)
|
||||
-- "SHOULD NOT have the significance of taking an action other than retrieval"
|
||||
-- The W3C has published guidance principles on this distinction, saying,
|
||||
-- "Web application design should be informed by the above principles,
|
||||
-- but also by the relevant limitations."
|
||||
|
||||
method_trace: STRING = "TRACE"
|
||||
-- Echoes back the received request, so that a client can see what
|
||||
-- (if any) changes or additions have been made by intermediate servers.
|
||||
|
||||
method_options: STRING = "OPTIONS"
|
||||
-- Returns the HTTP methods that the server supports for specified URL.
|
||||
-- This can be used to check the functionality of a web server by requesting '*'
|
||||
-- instead of a specific resource.
|
||||
|
||||
feature -- Methods intented for actions
|
||||
|
||||
method_post: STRING = "POST"
|
||||
-- Submits data to be processed (e.g., from an HTML form) to the identified resource.
|
||||
-- The data is included in the body of the request.
|
||||
-- This may result in the creation of a new resource or the updates of existing
|
||||
-- resources or both.
|
||||
|
||||
method_put: STRING = "PUT"
|
||||
-- Uploads a representation of the specified resource.
|
||||
|
||||
method_delete: STRING = "DELETE"
|
||||
-- Deletes the specified resource.
|
||||
|
||||
feature -- Other Methods
|
||||
|
||||
method_connect: STRING = "CONNECT"
|
||||
-- Converts the request connection to a transparent TCP/IP tunnel,
|
||||
-- usually to facilitate SSL-encrypted communication (HTTPS) through
|
||||
-- an unencrypted HTTP proxy.
|
||||
|
||||
method_patch: STRING = "PATCH"
|
||||
-- Is used to apply partial modifications to a resource
|
||||
|
||||
note
|
||||
copyright: "2011-2012, 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
|
||||
106
library/network/protocol/http/src/http_status_code.e
Normal file
106
library/network/protocol/http/src/http_status_code.e
Normal file
@@ -0,0 +1,106 @@
|
||||
note
|
||||
description: "[
|
||||
Status code constants pertaining to the HTTP protocol
|
||||
See http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
||||
]"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_STATUS_CODE
|
||||
|
||||
feature -- 1xx : Informational
|
||||
|
||||
continue: INTEGER = 100
|
||||
switching_protocols: INTEGER = 101
|
||||
processing: INTEGER = 102 -- WebDAV RFC 2518
|
||||
ie7_request_uri_too_long: INTEGER = 122 -- non standard, IE7 only
|
||||
|
||||
feature -- 2xx : Success
|
||||
|
||||
ok: INTEGER = 200
|
||||
created: INTEGER = 201
|
||||
accepted: INTEGER = 202
|
||||
nonauthoritative_info: INTEGER = 203
|
||||
no_content: INTEGER = 204
|
||||
reset_content: INTEGER = 205
|
||||
partial_content: INTEGER = 206
|
||||
multistatus: INTEGER = 207 -- WebDAV RFC 4918
|
||||
im_used: INTEGER = 226 -- RFC 4918
|
||||
|
||||
feature -- 3xx : Redirection
|
||||
|
||||
multiple_choices: INTEGER = 300
|
||||
moved_permanently: INTEGER = 301
|
||||
found: INTEGER = 302
|
||||
see_other: INTEGER = 303
|
||||
not_modified: INTEGER = 304
|
||||
use_proxy: INTEGER = 305
|
||||
switch_proxy: INTEGER = 306
|
||||
temp_redirect: INTEGER = 307
|
||||
|
||||
feature -- 4xx : Client Error
|
||||
|
||||
bad_request: INTEGER = 400
|
||||
unauthorized: INTEGER = 401
|
||||
payment_required: INTEGER = 402
|
||||
forbidden: INTEGER = 403
|
||||
not_found: INTEGER = 404
|
||||
method_not_allowed: INTEGER = 405
|
||||
not_acceptable: INTEGER = 406
|
||||
proxy_auth_required: INTEGER = 407
|
||||
request_timeout: INTEGER = 408
|
||||
conflict: INTEGER = 409
|
||||
gone: INTEGER = 410
|
||||
length_required: INTEGER = 411
|
||||
precondition_failed: INTEGER = 412
|
||||
request_entity_too_large: INTEGER = 413
|
||||
request_uri_too_long: INTEGER = 414
|
||||
unsupported_media_type: INTEGER = 415
|
||||
request_range_not_satisfiable: INTEGER = 416
|
||||
expectation_failed: INTEGER = 417
|
||||
teapot: INTEGER = 418
|
||||
|
||||
feature -- 4xx : Client Error : WebDAV errors
|
||||
|
||||
too_many_connections: INTEGER = 421
|
||||
unprocessable_entity: INTEGER = 422
|
||||
locked: INTEGER = 423
|
||||
failed_dependency: INTEGER = 424
|
||||
unordered_collection: INTEGER = 425
|
||||
|
||||
upgrade_required: INTEGER = 426
|
||||
no_response: INTEGER = 444
|
||||
retry_with: INTEGER = 449
|
||||
blocked_parental: INTEGER = 450
|
||||
client_closed_request: INTEGER = 499
|
||||
|
||||
feature -- 5xx : Server Error
|
||||
|
||||
internal_server_error: INTEGER = 500
|
||||
not_implemented: INTEGER = 501
|
||||
bad_gateway: INTEGER = 502
|
||||
service_unavailable: INTEGER = 503
|
||||
gateway_timeout: INTEGER = 504
|
||||
http_version_not_supported: INTEGER = 505
|
||||
variant_also_negotiates: INTEGER = 506
|
||||
insufficient_storage: INTEGER = 507 -- WebDAV RFC 4918
|
||||
|
||||
bandwidth_limit_exceeded: INTEGER = 509
|
||||
not_extended: INTEGER = 510
|
||||
|
||||
user_access_denied: INTEGER = 530
|
||||
|
||||
note
|
||||
copyright: "2011-2012, 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
|
||||
160
library/network/protocol/http/src/http_status_code_messages.e
Normal file
160
library/network/protocol/http/src/http_status_code_messages.e
Normal file
@@ -0,0 +1,160 @@
|
||||
note
|
||||
description: "[
|
||||
Status code constants pertaining to the HTTP protocol
|
||||
See http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
||||
]"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_STATUS_CODE_MESSAGES
|
||||
|
||||
inherit
|
||||
HTTP_STATUS_CODE
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_valid_http_status_code (v: INTEGER): BOOLEAN
|
||||
-- Is the given value a valid http status code?
|
||||
do
|
||||
Result := v >= continue and v <= user_access_denied
|
||||
end
|
||||
|
||||
feature -- Status messages
|
||||
|
||||
http_status_code_message (a_code: INTEGER): detachable STRING
|
||||
-- Header message related to HTTP status code `a_code'
|
||||
do
|
||||
inspect a_code
|
||||
when continue then
|
||||
Result := "Continue"
|
||||
when switching_protocols then
|
||||
Result := "Switching Protocols"
|
||||
when processing then
|
||||
Result := "Processing"
|
||||
when ok then
|
||||
Result := "OK"
|
||||
when created then
|
||||
Result := "Created"
|
||||
when accepted then
|
||||
Result := "Accepted"
|
||||
when nonauthoritative_info then
|
||||
Result := "Non-Authoritative Information"
|
||||
when no_content then
|
||||
Result := "No Content"
|
||||
when reset_content then
|
||||
Result := "Reset Content"
|
||||
when partial_content then
|
||||
Result := "Partial Content"
|
||||
when multistatus then
|
||||
Result := "Multi-Status"
|
||||
when multiple_choices then
|
||||
Result := "Multiple Choices"
|
||||
when moved_permanently then
|
||||
Result := "Moved Permanently"
|
||||
when found then
|
||||
Result := "Found"
|
||||
when see_other then
|
||||
Result := "See Other"
|
||||
when not_modified then
|
||||
Result := "Not Modified"
|
||||
when use_proxy then
|
||||
Result := "Use Proxy"
|
||||
when switch_proxy then
|
||||
Result := "Switch Proxy"
|
||||
when temp_redirect then
|
||||
Result := "Temporary Redirect"
|
||||
when bad_request then
|
||||
Result := "Bad Request"
|
||||
when unauthorized then
|
||||
Result := "Unauthorized"
|
||||
when payment_required then
|
||||
Result := "Payment Required"
|
||||
when forbidden then
|
||||
Result := "Forbidden"
|
||||
when not_found then
|
||||
Result := "Not Found"
|
||||
when method_not_allowed then
|
||||
Result := "Method Not Allowed"
|
||||
when not_acceptable then
|
||||
Result := "Not Acceptable"
|
||||
when proxy_auth_required then
|
||||
Result := "Proxy Authentication Required"
|
||||
when request_timeout then
|
||||
Result := "Request Timeout"
|
||||
when conflict then
|
||||
Result := "Conflict"
|
||||
when gone then
|
||||
Result := "Gone"
|
||||
when length_required then
|
||||
Result := "Length Required"
|
||||
when precondition_failed then
|
||||
Result := "Precondition Failed"
|
||||
when request_entity_too_large then
|
||||
Result := "Request Entity Too Large"
|
||||
when request_uri_too_long then
|
||||
Result := "Request-URI Too Long"
|
||||
when unsupported_media_type then
|
||||
Result := "Unsupported Media Type"
|
||||
when request_range_not_satisfiable then
|
||||
Result := "Requested Range Not Satisfiable"
|
||||
when expectation_failed then
|
||||
Result := "Expectation Failed"
|
||||
when teapot then
|
||||
Result := "I'm a teapot"
|
||||
when too_many_connections then
|
||||
Result := "There are too many connections from your internet address"
|
||||
when unprocessable_entity then
|
||||
Result := "Unprocessable Entity"
|
||||
when locked then
|
||||
Result := "Locked"
|
||||
when failed_dependency then
|
||||
Result := "Failed Dependency"
|
||||
when unordered_collection then
|
||||
Result := "Unordered Collection"
|
||||
when upgrade_required then
|
||||
Result := "Upgrade Required"
|
||||
when retry_with then
|
||||
Result := "Retry With"
|
||||
when blocked_parental then
|
||||
Result := "Blocked by Windows Parental Controls"
|
||||
when internal_server_error then
|
||||
Result := "Internal Server Error"
|
||||
when not_implemented then
|
||||
Result := "Not Implemented"
|
||||
when bad_gateway then
|
||||
Result := "Bad Gateway"
|
||||
when service_unavailable then
|
||||
Result := "Service Unavailable"
|
||||
when gateway_timeout then
|
||||
Result := "Gateway Timeout"
|
||||
when http_version_not_supported then
|
||||
Result := "HTTP Version Not Supported"
|
||||
when variant_also_negotiates then
|
||||
Result := "Variant Also Negotiates"
|
||||
when insufficient_storage then
|
||||
Result := "Insufficient Storage"
|
||||
when bandwidth_limit_exceeded then
|
||||
Result := "Bandwidth Limit Exceeded"
|
||||
when not_extended then
|
||||
Result := "Not Extended"
|
||||
when user_access_denied then
|
||||
Result := "User access denied"
|
||||
else
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, 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
|
||||
Reference in New Issue
Block a user