Better support for unicode path and values.

Added WSF_REQUEST.percent_encoded_path_info: READABLE_STRING_8
    to keep url encoded path info, as it is useful for specific component

The router is now using WSF_REQUEST.percent_encoded_path_info
    since URI_TEMPLATE are handling URI (and not IRI)
    this fixes an issue with unicode path parameters.

This should not break existing code, and this fixes various unicode related issues related
   to PATH parameter and path info
   but also any component using file names.

(required EiffelStudio >= 7.2)
This commit is contained in:
2013-06-12 18:00:55 +02:00
parent a982286dd4
commit cc4ef1a575
28 changed files with 1056 additions and 449 deletions

View File

@@ -10,6 +10,10 @@ class
inherit inherit
WSF_URI_TEMPLATE_RESPONSE_HANDLER WSF_URI_TEMPLATE_RESPONSE_HANDLER
SHARED_WSF_PERCENT_ENCODER
rename
percent_encoder as url_encoder
end
feature -- Access feature -- Access
@@ -75,7 +79,8 @@ feature -- Access
url_encoded_string (s: READABLE_STRING_32): STRING_8 url_encoded_string (s: READABLE_STRING_32): STRING_8
do do
Result := (create {UTF8_URL_ENCODER}).encoded_string (s) create Result.make (s.count)
url_encoder.append_percent_encoded_string_to (s, Result)
end end
html_decoded_string (v: READABLE_STRING_32): READABLE_STRING_32 html_decoded_string (v: READABLE_STRING_32): READABLE_STRING_32

View File

@@ -33,8 +33,7 @@ feature {NONE} -- Initialization
-- Create with no mapping -- Create with no mapping
-- but one can use `map' to add new mapping -- but one can use `map' to add new mapping
do do
create mapping.make (n) create mapping.make_caseless (n)
mapping.compare_objects
end end
make_default make_default
@@ -43,9 +42,8 @@ feature {NONE} -- Initialization
local local
m: like mapping m: like mapping
do do
create m.make (40) create m.make_caseless (40)
mapping := m mapping := m
m.compare_objects
m.force (text_css, "css") m.force (text_css, "css")
m.force (text_html, "html") m.force (text_html, "html")
m.force (text_xml, "xml") m.force (text_xml, "xml")
@@ -74,13 +72,13 @@ feature {NONE} -- Initialization
m.force (text_plain, "txt") m.force (text_plain, "txt")
end end
make_from_file (fn: READABLE_STRING_8) make_from_file (fn: READABLE_STRING_GENERAL)
-- Create with mime.types file -- Create with mime.types file
-- One can use `map' to add new mapping -- One can use `map' to add new mapping
local local
f: RAW_FILE f: RAW_FILE
do do
create f.make (fn) create f.make_with_name (fn)
if f.exists and then f.is_readable then if f.exists and then f.is_readable then
make_empty (50) make_empty (50)
f.open_read f.open_read
@@ -128,7 +126,7 @@ feature {NONE} -- Initialization
feature -- Access feature -- Access
mime_type (ext: READABLE_STRING_8): detachable READABLE_STRING_8 mime_type (ext: READABLE_STRING_GENERAL): detachable READABLE_STRING_8
-- Mime type for extension `ext' -- Mime type for extension `ext'
do do
Result := mapping.item (ext.as_lower) Result := mapping.item (ext.as_lower)
@@ -136,7 +134,7 @@ feature -- Access
feature -- Element change feature -- Element change
map (e: READABLE_STRING_8; t: READABLE_STRING_8) map (e: READABLE_STRING_GENERAL; t: READABLE_STRING_8)
-- Add mapping extension `e' to mime type `t' -- Add mapping extension `e' to mime type `t'
do do
mapping.force (t, e.as_lower) mapping.force (t, e.as_lower)
@@ -220,13 +218,13 @@ feature {NONE} -- Implementation
feature {NONE} -- Extension MIME mapping feature {NONE} -- Extension MIME mapping
mapping: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] mapping: STRING_TABLE [READABLE_STRING_8]
invariant invariant
mapping_keys_are_lowercase: across mapping as c all c.key.same_string (c.key.as_lower) end mapping_keys_are_lowercase: across mapping as c all c.key.same_string (c.key.as_lower) end
note note
copyright: "2011-2011, Eiffel Software and others" copyright: "2011-2013, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -137,7 +137,7 @@ feature {WSF_RESPONSE} -- Output
debug debug
l_description.append ("<h2>Meta Information</h2><ul>") l_description.append ("<h2>Meta Information</h2><ul>")
l_description.append ("<li>PATH_INFO=" + request.path_info + "</li>") l_description.append ("<li>PATH_INFO=" + request.percent_encoded_path_info + "</li>")
l_description.append ("<li>QUERY_STRING=" + request.query_string + "</li>") l_description.append ("<li>QUERY_STRING=" + request.query_string + "</li>")
l_description.append ("<li>REQUEST_URI=" + request.request_uri + "</li>") l_description.append ("<li>REQUEST_URI=" + request.request_uri + "</li>")
l_description.append ("<li>SCRIPT_NAME=" + request.script_name + "</li>") l_description.append ("<li>SCRIPT_NAME=" + request.script_name + "</li>")

View File

@@ -20,7 +20,7 @@ feature -- Execution
a_start_path_attached: a_start_path /= Void a_start_path_attached: a_start_path /= Void
req_attached: req /= Void req_attached: req /= Void
res_attached: res /= Void res_attached: res /= Void
path_start_with_a_start_path: req.path_info.starts_with (a_start_path) path_start_with_a_start_path: req.percent_encoded_path_info.starts_with (a_start_path)
deferred deferred
end end
@@ -33,7 +33,7 @@ feature {WSF_ROUTER} -- Mapping
end end
note note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -83,7 +83,7 @@ feature {NONE} -- Execution
a_start_path_attached: a_start_path /= Void a_start_path_attached: a_start_path /= Void
req_attached: req /= Void req_attached: req /= Void
res_attached: res /= Void res_attached: res /= Void
path_start_with_a_start_path: req.path_info.starts_with (a_start_path) path_start_with_a_start_path: req.percent_encoded_path_info.starts_with (a_start_path)
deferred deferred
end end

View File

@@ -61,7 +61,7 @@ feature -- Status
-- <Precursor> -- <Precursor>
local local
tpl: URI_TEMPLATE tpl: URI_TEMPLATE
p: READABLE_STRING_32 p: READABLE_STRING_8
do do
p := path_from_request (req) p := path_from_request (req)
tpl := based_uri_template (template, a_router) tpl := based_uri_template (template, a_router)
@@ -72,7 +72,7 @@ feature -- Status
-- <Precursor> -- <Precursor>
local local
tpl: URI_TEMPLATE tpl: URI_TEMPLATE
p: READABLE_STRING_32 p: READABLE_STRING_8
new_src: detachable WSF_REQUEST_PATH_PARAMETERS_PROVIDER new_src: detachable WSF_REQUEST_PATH_PARAMETERS_PROVIDER
do do
p := path_from_request (req) p := path_from_request (req)

View File

@@ -15,31 +15,53 @@ inherit
WSF_SELF_DOCUMENTED_HANDLER WSF_SELF_DOCUMENTED_HANDLER
SHARED_HTML_ENCODER
SHARED_WSF_PERCENT_ENCODER
rename
percent_encoder as url_encoder
export
{NONE} all
end
SHARED_EXECUTION_ENVIRONMENT
export
{NONE} all
end
create create
make_with_path,
make_hidden_with_path,
make, make,
make_hidden make_hidden
feature {NONE} -- Initialization feature {NONE} -- Initialization
make (d: like document_root) make_with_path (d: like document_root)
require
valid_d: (d /= Void and then not d.is_empty) implies not d.ends_with (operating_environment.directory_separator.out)
local
e: EXECUTION_ENVIRONMENT
do do
if d.is_empty then if d.is_empty then
create e document_root := execution_environment.current_working_path
document_root := e.current_working_directory
else else
document_root := d document_root := d
end end
ensure ensure
not document_root.is_empty and then not document_root.ends_with (operating_environment.directory_separator.out) not document_root.is_empty
end end
make_hidden (d: like document_root) make_hidden_with_path (d: like document_root)
require do
valid_d: (d /= Void and then not d.is_empty) implies not d.ends_with (operating_environment.directory_separator.out) make_with_path (d)
is_hidden := True
ensure
hidden: is_hidden
end
make (d: READABLE_STRING_GENERAL)
do
make_with_path (create {PATH}.make_from_string (d))
end
make_hidden (d: READABLE_STRING_GENERAL)
do do
make (d) make (d)
is_hidden := True is_hidden := True
@@ -60,9 +82,10 @@ feature -- Documentation
Result.add_description ("File service") Result.add_description ("File service")
end end
feature -- Access feature -- Access
document_root: PATH
document_root: STRING
max_age: INTEGER max_age: INTEGER
index_disabled: BOOLEAN index_disabled: BOOLEAN
@@ -118,10 +141,10 @@ feature -- Execution
execute (a_start_path: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE) execute (a_start_path: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
p: STRING p: STRING_32
do do
p := req.path_info create p.make_from_string (req.path_info)
if p.starts_with (a_start_path) then if p.starts_with_general (a_start_path) then
p.remove_head (a_start_path.count) p.remove_head (a_start_path.count)
else else
check starts_with_base: False end check starts_with_base: False end
@@ -131,16 +154,16 @@ feature -- Execution
execute_starts_with (a_start_path: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE) execute_starts_with (a_start_path: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
do do
execute (a_start_path,req, res) execute (a_start_path, req, res)
end end
process_uri (uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE) process_uri (uri: READABLE_STRING_32; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
f: RAW_FILE f: RAW_FILE
fn: READABLE_STRING_8 fn: like resource_filename
do do
fn := resource_filename (uri) fn := resource_filename (uri)
create f.make (fn) create f.make_with_path (fn)
if f.exists then if f.exists then
if f.is_readable then if f.is_readable then
if f.is_directory then if f.is_directory then
@@ -160,14 +183,15 @@ feature -- Execution
end end
end end
process_index (a_uri: READABLE_STRING_8; dn: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE) process_index (a_uri: READABLE_STRING_8; dn: PATH; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
h: HTTP_HEADER h: HTTP_HEADER
uri, s: STRING_8 uri, s: STRING_8
d: DIRECTORY d: DIRECTORY
l_files: LIST [STRING_8] l_files: LIST [PATH]
do do
create d.make_open_read (dn) create d.make_with_path (dn)
d.open_read
if attached directory_index_file (d) as f then if attached directory_index_file (d) as f then
process_file (f, req, res) process_file (f, req, res)
else else
@@ -187,12 +211,16 @@ feature -- Execution
s.replace_substring_all ("$URI", uri) s.replace_substring_all ("$URI", uri)
from from
l_files := d.linear_representation l_files := d.entries
l_files.start l_files.start
until until
l_files.after l_files.after
loop loop
s.append ("<li><a href=%"" + uri + l_files.item_for_iteration + "%">" + l_files.item_for_iteration + "</a></li>%N") s.append ("<li><a href=%"" + uri)
url_encoder.append_percent_encoded_string_to (l_files.item.name, s)
s.append ("%">")
s.append (html_encoder.encoded_string (l_files.item.name))
s.append ("</a></li>%N")
l_files.forth l_files.forth
end end
s.append ("[ s.append ("[
@@ -217,12 +245,12 @@ feature -- Execution
process_file (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE) process_file (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE)
local local
ext: READABLE_STRING_8 ext: READABLE_STRING_32
ct: detachable READABLE_STRING_8 ct: detachable READABLE_STRING_8
fres: WSF_FILE_RESPONSE fres: WSF_FILE_RESPONSE
dt: DATE_TIME dt: DATE_TIME
do do
ext := extension (f.name) ext := extension (f.path.name)
ct := extension_mime_mapping.mime_type (ext) ct := extension_mime_mapping.mime_type (ext)
if ct = Void then if ct = Void then
ct := {HTTP_MIME_TYPES}.application_force_download ct := {HTTP_MIME_TYPES}.application_force_download
@@ -235,7 +263,7 @@ feature -- Execution
then then
process_not_modified (f_date, req, res) process_not_modified (f_date, req, res)
else else
create fres.make_with_content_type (ct, f.name) create fres.make_with_content_type (ct, f.path.name)
fres.set_status_code ({HTTP_STATUS_CODE}.ok) fres.set_status_code ({HTTP_STATUS_CODE}.ok)
-- cache control -- cache control
@@ -341,7 +369,7 @@ feature {NONE} -- Implementation
directory_index_file (d: DIRECTORY): detachable FILE directory_index_file (d: DIRECTORY): detachable FILE
local local
f: detachable RAW_FILE f: detachable RAW_FILE
fn: FILE_NAME fn: PATH
do do
if attached directory_index as default_index then if attached directory_index as default_index then
across across
@@ -350,12 +378,11 @@ feature {NONE} -- Implementation
Result /= Void Result /= Void
loop loop
if d.has_entry (c.item) then if d.has_entry (c.item) then
create fn.make_from_string (d.name) fn := d.path.extended (c.item)
fn.set_file_name (c.item)
if f = Void then if f = Void then
create f.make (fn.string) create f.make_with_path (fn)
else else
f.make (fn.string) f.make_with_path (fn)
end end
if f.exists and then f.is_readable then if f.exists and then f.is_readable then
Result := f Result := f
@@ -365,28 +392,34 @@ feature {NONE} -- Implementation
end end
end end
resource_filename (uri: READABLE_STRING_8): READABLE_STRING_8 resource_filename (uri: READABLE_STRING_32): PATH
do
Result := real_filename (document_root + operating_environment.directory_separator.out + real_filename (uri))
end
dirname (uri: READABLE_STRING_8): READABLE_STRING_8
local local
p: INTEGER s: like uri_path_to_filename
do do
p := uri.last_index_of ('/', uri.count) Result := document_root
if p > 0 then s := uri_path_to_filename (uri)
Result := uri.substring (1, p - 1) if not s.is_empty then
else Result := Result.extended (s)
create {STRING_8} Result.make_empty
end end
end end
filename (uri: READABLE_STRING_8): READABLE_STRING_8 dirname (uri: READABLE_STRING_32): READABLE_STRING_32
local local
p: INTEGER p: INTEGER
do do
p := uri.last_index_of ('/', uri.count) p := uri.last_index_of ({CHARACTER_32} '/', uri.count)
if p > 0 then
Result := uri.substring (1, p - 1)
else
create {STRING_32} Result.make_empty
end
end
filename (uri: READABLE_STRING_32): READABLE_STRING_32
local
p: INTEGER
do
p := uri.last_index_of ({CHARACTER_32} '/', uri.count)
if p > 0 then if p > 0 then
Result := uri.substring (p + 1, uri.count) Result := uri.substring (p + 1, uri.count)
else else
@@ -394,58 +427,52 @@ feature {NONE} -- Implementation
end end
end end
extension (uri: READABLE_STRING_8): READABLE_STRING_8 extension (uri: READABLE_STRING_32): READABLE_STRING_32
local local
p: INTEGER p: INTEGER
do do
p := uri.last_index_of ('.', uri.count) p := uri.last_index_of ({CHARACTER_32} '.', uri.count)
if p > 0 then if p > 0 then
Result := uri.substring (p + 1, uri.count) Result := uri.substring (p + 1, uri.count)
else else
create {STRING_8} Result.make_empty create {STRING_32} Result.make_empty
end end
end end
real_filename (fn: STRING): STRING uri_path_to_filename (fn: READABLE_STRING_32): STRING_32
-- Real filename from url-path `fn' -- Real filename from url-path `fn'
--| Find a better design for this piece of code --| Find a better design for this piece of code
--| Eventually in a spec/$ISE_PLATFORM/ specific cluster --| Eventually in a spec/$ISE_PLATFORM/ specific cluster
local
n: INTEGER
do do
if fn.is_empty then n := fn.count
Result := fn create Result.make_from_string (fn)
else if n > 0 and then Result.item (Result.count) = {CHARACTER_32} '/' then
Result.remove_tail (1)
n := n - 1
end
if n > 0 and then Result.item (1) = {CHARACTER_32} '/' then
Result.remove_head (1)
n := n - 1
end
if n > 0 then
if {PLATFORM}.is_windows then if {PLATFORM}.is_windows then
create Result.make_from_string (fn) Result.replace_substring_all ({STRING_32} "/", {STRING_32} "\")
Result.replace_substring_all ("/", "\")
if Result [Result.count] = '\' then
Result.remove_tail (1)
end
else
Result := fn
if Result [Result.count] = '/' then
Result.remove_tail (1)
end
end end
end end
end end
feature {NONE} -- Implementation feature {NONE} -- Implementation
node_exists (p: READABLE_STRING_8): BOOLEAN
local
f: RAW_FILE
do
create f.make (p)
Result := f.exists
end
extension_mime_mapping: HTTP_FILE_EXTENSION_MIME_MAPPING extension_mime_mapping: HTTP_FILE_EXTENSION_MIME_MAPPING
local local
f: RAW_FILE f: RAW_FILE
once once
create f.make ("mime.types") create f.make_with_name ("mime.types")
if f.exists and then f.is_readable then if f.exists and then f.is_readable then
create Result.make_from_file (f.name) create Result.make_from_file (f.path.name)
else else
create Result.make_default create Result.make_default
end end

View File

@@ -77,12 +77,12 @@ feature -- Status
feature -- Helper feature -- Helper
path_from_request (req: WSF_REQUEST): READABLE_STRING_32 path_from_request (req: WSF_REQUEST): READABLE_STRING_8
-- Path used by `Current' to check that mapping matches request `req' -- Path used by `Current' to check that mapping matches request `req'
require require
req_attached: req /= Void req_attached: req /= Void
do do
Result := req.path_info Result := req.percent_encoded_path_info
ensure ensure
path_from_request_attached: Result /= Void path_from_request_attached: Result /= Void
end end

View File

@@ -10,6 +10,11 @@ class
inherit inherit
WSF_SESSION WSF_SESSION
SHARED_EXECUTION_ENVIRONMENT
export
{NONE} all
end
create create
make, make,
make_new make_new
@@ -128,15 +133,6 @@ feature {NONE} -- Storage
data.compare_objects data.compare_objects
end end
sessions_folder_name: READABLE_STRING_8
local
dn: DIRECTORY_NAME
once
create dn.make_from_string ((create {EXECUTION_ENVIRONMENT}).current_working_directory)
dn.extend ("_sessions_")
Result := dn.string
end
load load
do do
if manager.session_exists (uuid) then if manager.session_exists (uuid) then
@@ -181,7 +177,7 @@ feature {NONE} -- Implementation
end end
note note
copyright: "Copyright (c) 1984-2012, Eiffel Software and others" copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -21,12 +21,12 @@ feature {NONE} -- Initialization
make_with_folder ("_WSF_SESSIONS_") make_with_folder ("_WSF_SESSIONS_")
end end
make_with_folder (a_folder: like sessions_folder_name) make_with_folder (a_folder: READABLE_STRING_GENERAL)
do do
sessions_folder_name := a_folder create sessions_folder_name.make_from_string (a_folder)
end end
sessions_folder_name: STRING_8 sessions_folder_name: PATH
feature -- Access feature -- Access
@@ -34,7 +34,7 @@ feature -- Access
local local
f: RAW_FILE f: RAW_FILE
do do
create f.make (file_name (a_session_uuid)) create f.make_with_path (file_name (a_session_uuid))
Result := f.exists and then f.is_readable Result := f.exists and then f.is_readable
end end
@@ -42,7 +42,7 @@ feature -- Access
local local
f: RAW_FILE f: RAW_FILE
do do
create f.make (file_name (a_session_uuid)) create f.make_with_path (file_name (a_session_uuid))
if f.exists and then f.is_readable then if f.exists and then f.is_readable then
f.open_read f.open_read
if attached data_from_file (f) as d then if attached data_from_file (f) as d then
@@ -68,7 +68,7 @@ feature -- Persistence
delete_session (a_session) delete_session (a_session)
else else
ensure_session_folder_exists ensure_session_folder_exists
create f.make (file_name (a_session.uuid)) create f.make_with_path (file_name (a_session.uuid))
if not f.exists or else f.is_writable then if not f.exists or else f.is_writable then
f.create_read_write f.create_read_write
a_session.data.set_expiration (a_session.expiration) a_session.data.set_expiration (a_session.expiration)
@@ -91,7 +91,7 @@ feature -- Persistence
rescued: BOOLEAN rescued: BOOLEAN
do do
if not rescued then if not rescued then
create f.make (file_name (a_session.uuid)) create f.make_with_path (file_name (a_session.uuid))
if f.exists then if f.exists then
f.delete f.delete
end end
@@ -131,7 +131,7 @@ feature {NONE} -- Implementation
local local
d: DIRECTORY d: DIRECTORY
once once
create d.make (sessions_folder_name) create d.make_with_path (sessions_folder_name)
if not d.exists then if not d.exists then
d.recursive_create_dir d.recursive_create_dir
end end
@@ -143,18 +143,13 @@ feature {NONE} -- Implementation
local local
d: DIRECTORY d: DIRECTORY
do do
create d.make (sessions_folder_name) create d.make_with_path (sessions_folder_name)
Result := d.exists and then d.is_writable Result := d.exists and then d.is_writable
end end
file_name (a_uuid: like {WSF_SESSION}.uuid): READABLE_STRING_8 file_name (a_uuid: like {WSF_SESSION}.uuid): PATH
local
fn: FILE_NAME
do do
create fn.make_from_string (sessions_folder_name) Result := sessions_folder_name.extended (a_uuid.out).appended_with_extension ("session")
fn.set_file_name (a_uuid.out)
fn.add_extension ("session")
Result := fn.string
end end
note note

View File

@@ -0,0 +1,28 @@
note
description: "Objects to access the shared once WSF_PERCENT_ENCODER ..."
date: "$Date$"
revision: "$Revision$"
class
SHARED_WSF_PERCENT_ENCODER
feature -- Encoder
percent_encoder: WSF_PERCENT_ENCODER
-- Shared Percent encoding engine.
once
create Result
end
note
copyright: "2011-2013, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,523 @@
note
description: "[
Component to handle percent encoding
]"
date: "$Date: 2013-05-21 01:15:17 +0200 (mar., 21 mai 2013) $"
revision: "$Revision: 92557 $"
EIS: "name=Percent-encoding", "protocol=URI", "src=http://en.wikipedia.org/wiki/Percent-encoding"
class
WSF_PERCENT_ENCODER
feature -- Percent encoding
percent_encoded_string (v: READABLE_STRING_GENERAL): STRING_8
-- Return `a_string' percent-encoded
do
create Result.make (v.count)
append_percent_encoded_string_to (v, Result)
end
append_percent_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL)
-- Append `a_string' as percent-encoded value to `a_result'
local
c: NATURAL_32
i,n: INTEGER
do
from
i := 1
n := s.count
until
i > n
loop
c := s.code (i)
if
--| unreserved ALPHA / DIGIT
(48 <= c and c <= 57) -- DIGIT: 0 .. 9
or (65 <= c and c <= 90) -- ALPHA: A .. Z
or (97 <= c and c <= 122) -- ALPHA: a .. z
then
a_result.append_code (c)
else
inspect c
when
45, 46, 95, 126 -- unreserved characters: -._~
then
a_result.append_code (c)
when
58, 64, -- reserved =+ gen-delims: :@
33, 36, 38, 39, 40, 41, 42, -- reserved =+ sub-delims: !$&'()*
43, 44, 59, 61, -- reserved = sub-delims: +,;=
37 -- percent encoding: %
then
append_percent_encoded_character_code_to (c, a_result)
else
append_percent_encoded_character_code_to (c, a_result)
end
end
i := i + 1
end
end
feature -- Percent encoding: character
append_percent_encoded_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL)
-- Append character code `a_code' as percent-encoded content into `a_result'
do
if a_code > 0xFF then
-- Unicode
append_percent_encoded_unicode_character_code_to (a_code, a_result)
elseif a_code > 0x7F then
-- Extended ASCII
-- This requires percent-encoding on UTF-8 converted character.
append_percent_encoded_unicode_character_code_to (a_code, a_result)
else
-- ASCII
append_percent_encoded_ascii_character_code_to (a_code, a_result)
end
ensure
appended: a_result.count > old a_result.count
end
feature {NONE} -- Implementation: character encoding
append_percent_encoded_ascii_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL)
-- Append extended ascii character code `a_code' as percent-encoded content into `a_result'
-- Note: it does not UTF-8 convert this extended ASCII.
require
is_extended_ascii: a_code <= 0xFF
local
c: INTEGER
do
if a_code > 0xFF then
-- Unicode
append_percent_encoded_unicode_character_code_to (a_code, a_result)
else
-- Extended ASCII
c := a_code.to_integer_32
a_result.append_code (37) -- 37 '%%'
a_result.append_code (hex_digit [c |>> 4])
a_result.append_code (hex_digit [c & 0xF])
end
ensure
appended: a_result.count > old a_result.count
end
append_percent_encoded_unicode_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL)
-- Append Unicode character code `a_code' as UTF-8 and percent-encoded content into `a_result'
-- Note: it does include UTF-8 conversion of extended ASCII and Unicode.
do
if a_code <= 0x7F then
-- 0xxxxxxx
append_percent_encoded_ascii_character_code_to (a_code, a_result)
elseif a_code <= 0x7FF then
-- 110xxxxx 10xxxxxx
append_percent_encoded_ascii_character_code_to ((a_code |>> 6) | 0xC0, a_result)
append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result)
elseif a_code <= 0xFFFF then
-- 1110xxxx 10xxxxxx 10xxxxxx
append_percent_encoded_ascii_character_code_to ((a_code |>> 12) | 0xE0, a_result)
append_percent_encoded_ascii_character_code_to (((a_code |>> 6) & 0x3F) | 0x80, a_result)
append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result)
else
-- c <= 1FFFFF - there are no higher code points
-- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
append_percent_encoded_ascii_character_code_to ((a_code |>> 18) | 0xF0, a_result)
append_percent_encoded_ascii_character_code_to (((a_code |>> 12) & 0x3F) | 0x80, a_result)
append_percent_encoded_ascii_character_code_to (((a_code |>> 6) & 0x3F) | 0x80, a_result)
append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result)
end
ensure
appended: a_result.count > old a_result.count
end
feature -- Percent decoding
percent_decoded_string (v: READABLE_STRING_GENERAL): STRING_32
-- Return the percent decoded string equivalent to the percent-encoded string `v'
--| Note that is `a_result' is a STRING_8, any Unicode character will be kept as UTF-8
do
create Result.make (v.count)
append_percent_decoded_string_to (v, Result)
end
append_percent_decoded_string_to (v: READABLE_STRING_GENERAL; a_result: STRING_GENERAL)
-- Append to `a_result' a string equivalent to the percent-encoded string `v'
--| Note that is `a_result' is a STRING_8, any Unicode character will be kept as UTF-8
local
i,n: INTEGER
c: NATURAL_32
pr: CELL [INTEGER]
a_result_is_string_32: BOOLEAN
do
a_result_is_string_32 := attached {STRING_32} a_result
from
i := 1
create pr.put (i)
n := v.count
until
i > n
loop
c := v.code (i)
inspect c
when 43 then -- 43 '+'
-- Some implementation are replacing spaces with "+" instead of "%20"
a_result.append_code (32) -- 32 ' '
when 37 then -- 37 '%%'
-- An escaped character ?
if i = n then -- Error?
a_result.append_code (c)
else
if a_result_is_string_32 then
-- Convert UTF-8 to UTF-32
pr.replace (i)
c := next_percent_decoded_unicode_character_code (v, pr)
a_result.append_code (c)
i := pr.item
else
-- Keep UTF-8
pr.replace (i)
c := next_percent_decoded_character_code (v, pr)
a_result.append_code (c)
i := pr.item
end
end
else
if c <= 0x7F then
a_result.append_code (c)
else
if a_result_is_string_32 then
a_result.append_code (c)
else
append_percent_encoded_character_code_to (c, a_result)
end
end
end
i := i + 1
end
end
feature {NONE} -- Implementation: decoding
next_percent_decoded_character_code (v: READABLE_STRING_GENERAL; a_position: CELL [INTEGER]): NATURAL_32
-- Character decoded from string `v' starting from index `a_position.item'
-- note: it also updates `a_position.item' to indicate the new index position.
require
valid_start: a_position.item <= v.count
is_percent_char: v.code (a_position.item) = 37 -- 37 '%%'
local
c: NATURAL_32
i, n: INTEGER
not_a_digit: BOOLEAN
ascii_pos: NATURAL_32
ival: NATURAL_32
pos: INTEGER
c_is_digit: BOOLEAN
do
--| pos is index in stream of escape character ('%')
pos := a_position.item
c := v.code (pos + 1)
if c = 85 or c = 117 then -- 117 'u' 85 'U'
-- NOTE: this is not a standard, but it can occur, so use this for decoding only
-- An escaped Unicode (ucs2) value, from ECMA scripts
-- has the form: %u<n> where <n> is the UCS value
-- of the character (two byte integer, one to 4 chars
-- after escape sequence).
-- See: http://en.wikipedia.org/wiki/Percent-encoding#Non-standard_implementations
-- UTF-8 result can be 1 to 4 characters.
from
i := pos + 2
n := v.count
until
(i > n) or not_a_digit
loop
c := v.code (i)
c_is_digit := (48 <= c and c <= 57) -- DIGIT: 0 .. 9
if
c_is_digit
or (97 <= c and c <= 102) -- ALPHA: a..f
or (65 <= c and c <= 70) -- ALPHA: A..F
then
ival := ival * 16
if c_is_digit then
ival := ival + (c - 48) -- 48 '0'
else
if c > 70 then -- a..f
ival := ival + (c - 97) + 10 -- 97 'a'
else -- A..F
ival := ival + (c - 65) + 10 -- 65 'A'
end
end
i := i + 1
else
not_a_digit := True
i := i - 1
end
end
a_position.replace (i)
Result := ival
else
-- ASCII char?
ascii_pos := hexadecimal_string_to_natural_32 (v.substring (pos + 1, pos + 2))
Result := ascii_pos
a_position.replace (pos + 2)
end
end
next_percent_decoded_unicode_character_code (v: READABLE_STRING_GENERAL; a_position: CELL [INTEGER]): NATURAL_32
-- Next decoded character from `v' at position `a_position.item'
-- note: it also updates `a_position' to indicate the new index position.
require
valid_start: a_position.item <= v.count
is_percent_char: v.code (a_position.item) = 37 -- 37 '%%'
local
n, j: INTEGER
c: NATURAL_32
c1, c2, c3, c4: NATURAL_32
pr: CELL [INTEGER]
do
create pr.put (a_position.item)
c1 := next_percent_decoded_character_code (v, pr)
j := pr.item
n := v.count
Result := c1
a_position.replace (j)
if c1 <= 0x7F then
-- 0xxxxxxx
Result := c1
elseif c1 <= 0xDF then
-- 110xxxxx 10xxxxxx
if j + 2 <= n then
c := v.code (j + 1)
if c = 37 then -- 37 '%%'
pr.replace (j + 1)
c2 := next_percent_decoded_character_code (v, pr)
j := pr.item
Result := (
((c1 & 0x1F) |<< 6) |
( c2 & 0x3F )
)
a_position.replace (j)
else
-- Do not try to decode
end
end
elseif c1 <= 0xEF then
-- 1110xxxx 10xxxxxx 10xxxxxx
if j + 2 <= n then
c := v.code (j + 1)
if c = 37 then -- 37 '%%'
pr.replace (j + 1)
c2 := next_percent_decoded_character_code (v, pr)
j := pr.item
if j + 2 <= n then
c := v.code (j + 1)
if c = 37 then -- 37 '%%'
pr.replace (j + 1)
c3 := next_percent_decoded_character_code (v, pr)
j := pr.item
Result := (
((c1 & 0xF) |<< 12) |
((c2 & 0x3F) |<< 6) |
( c3 & 0x3F )
)
a_position.replace (j)
else
-- Do not try to decode
end
end
else
-- Do not try to decode
end
end
elseif c1 <= 0xF7 then
-- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
if j + 2 <= n then
c := v.code (j + 1)
if c = 37 then -- 37 '%%'
pr.replace (j + 1)
c2 := next_percent_decoded_character_code (v, pr)
j := pr.item
if j + 2 <= n then
c := v.code (j + 1)
if c = 37 then -- 37 '%%'
pr.replace (j + 1)
c3 := next_percent_decoded_character_code (v, pr)
j := pr.item
if j + 2 <= n then
c := v.code (j + 1)
if c = 37 then -- 37 '%%'
pr.replace (j + 1)
c4 := next_percent_decoded_character_code (v, pr)
j := pr.item
a_position.replace (j)
Result := (
((c1 & 0x7) |<< 18 ) |
((c2 & 0x3F) |<< 12) |
((c3 & 0x3F) |<< 6) |
( c4 & 0x3F )
)
else
-- Do not try to decode
end
end
else
-- Do not try to decode
end
end
else
-- Do not try to decode
end
end
else
Result := c1
end
end
feature -- RFC and characters
is_hexa_decimal_character (c: CHARACTER_32): BOOLEAN
-- Is hexadecimal character ?
do
Result := ('a' <= c and c <= 'f') or ('A' <= c and c <= 'F') -- HEXA
or ('0' <= c and c <= '9') -- DIGIT
end
is_alpha_or_digit_character (c: CHARACTER_32): BOOLEAN
-- Is ALPHA or DIGIT character ?
do
Result := ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z') -- ALPHA
or ('0' <= c and c <= '9') -- DIGIT
end
is_alpha_character (c: CHARACTER_32): BOOLEAN
-- Is ALPHA character ?
do
Result := ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z')
end
is_digit_character (c: CHARACTER_32): BOOLEAN
-- Is DIGIT character ?
do
Result := ('0' <= c and c <= '9')
end
is_unreserved_character (c: CHARACTER_32): BOOLEAN
-- unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
do
if
('a' <= c and c <= 'z') -- ALPHA
or ('A' <= c and c <= 'Z') -- ALPHA
or ('0' <= c and c <= '9') -- DIGIT
then
Result := True
else
inspect c
when '-', '_', '.', '~' then -- unreserved
Result := True
else
end
end
end
is_reserved_character (c: CHARACTER_32): BOOLEAN
-- reserved = gen-delims / sub-delims
do
Result := is_gen_delims_character (c) or is_sub_delims_character (c)
end
is_gen_delims_character (c: CHARACTER_32): BOOLEAN
-- gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
do
inspect c
when ':' , '/', '?' , '#' , '[' , ']' , '@' then
Result := True
else
end
end
is_sub_delims_character (c: CHARACTER_32): BOOLEAN
-- sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
-- / "*" / "+" / "," / ";" / "="
do
inspect c
when '!' , '$' , '&' , '%'' , '(' , ')' , '*' , '+' , ',' , ';' , '=' then -- sub-delims
Result := True
else
end
end
feature {NONE} -- Implementation
hex_digit: SPECIAL [NATURAL_32]
-- Hexadecimal digits.
once
create Result.make_filled (0, 16)
Result [0] := {NATURAL_32} 48 -- 48 '0'
Result [1] := {NATURAL_32} 49 -- 49 '1'
Result [2] := {NATURAL_32} 50 -- 50 '2'
Result [3] := {NATURAL_32} 51 -- 51 '3'
Result [4] := {NATURAL_32} 52 -- 52 '4'
Result [5] := {NATURAL_32} 53 -- 53 '5'
Result [6] := {NATURAL_32} 54 -- 54 '6'
Result [7] := {NATURAL_32} 55 -- 55 '7'
Result [8] := {NATURAL_32} 56 -- 56 '8'
Result [9] := {NATURAL_32} 57 -- 57 '9'
Result [10] := {NATURAL_32} 65 -- 65 'A'
Result [11] := {NATURAL_32} 66 -- 66 'B'
Result [12] := {NATURAL_32} 67 -- 67 'C'
Result [13] := {NATURAL_32} 68 -- 68 'D'
Result [14] := {NATURAL_32} 69 -- 69 'E'
Result [15] := {NATURAL_32} 70 -- 70 'F'
end
is_hexa_decimal (a_string: READABLE_STRING_GENERAL): BOOLEAN
-- Is `a_string' a valid hexadecimal sequence?
local
l_convertor: like ctoi_convertor
do
l_convertor := ctoi_convertor
l_convertor.parse_string_with_type (a_string, {NUMERIC_INFORMATION}.type_natural_32)
Result := l_convertor.is_integral_integer
end
hexadecimal_string_to_natural_32 (a_hex_string: READABLE_STRING_GENERAL): NATURAL_32
-- Convert hexadecimal value `a_hex_string' to its corresponding NATURAL_32 value.
require
is_hexa: is_hexa_decimal (a_hex_string)
local
l_convertor: like ctoi_convertor
do
l_convertor := ctoi_convertor
l_convertor.parse_string_with_type (a_hex_string, {NUMERIC_INFORMATION}.type_no_limitation)
Result := l_convertor.parsed_natural_32
end
ctoi_convertor: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
-- Converter used to convert string to integer or natural.
once
create Result.make
Result.set_leading_separators_acceptable (False)
Result.set_trailing_separators_acceptable (False)
ensure
ctoi_convertor_not_void: Result /= Void
end
note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -62,7 +62,7 @@ feature -- Access
url_encoded_name: READABLE_STRING_8 url_encoded_name: READABLE_STRING_8
-- URL encoded string of `name'. -- URL encoded string of `name'.
do do
Result := url_encoder.encoded_string (name) Result := url_encoded_string (name)
end end
values: LIST [WSF_STRING] values: LIST [WSF_STRING]

View File

@@ -50,20 +50,6 @@ feature -- Access
url_encoded_value: READABLE_STRING_8 url_encoded_value: READABLE_STRING_8
-- URL encoded string of `value'. -- URL encoded string of `value'.
frozen string: like value
obsolete
"Use value [2012-May-31]"
do
Result := value
end
frozen url_encoded_string: like url_encoded_value
obsolete
"Use url_encoded_value [2012-May-31]"
do
Result := url_encoded_value
end
feature -- Conversion feature -- Conversion
integer_value: INTEGER integer_value: INTEGER
@@ -137,7 +123,7 @@ feature -- Visitor
end end
note note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -36,17 +36,17 @@ feature -- Access
feature -- Status report feature -- Status report
debug_output: STRING debug_output: STRING_32
-- String that should be displayed in debugger to represent `Current'. -- String that should be displayed in debugger to represent `Current'.
do do
Result := Precursor Result := Precursor
if if
exists and then exists and then
attached tmp_name as n attached tmp_path as p
then then
Result.append_character (' ') Result.append_character (' ')
Result.append_character ('%"') Result.append_character ('%"')
Result.append (n) Result.append (p.name)
Result.append_character ('%"') Result.append_character ('%"')
end end
Result.append (" filename=%"") Result.append (" filename=%"")
@@ -108,9 +108,16 @@ feature -- Access: Uploaded File
size: INTEGER size: INTEGER
-- Size of uploaded file -- Size of uploaded file
tmp_name: detachable STRING tmp_path: detachable PATH
-- Filename of tmp file -- Filename of tmp file
tmp_name: detachable READABLE_STRING_GENERAL
do
if attached tmp_path as p then
Result := p.name
end
end
tmp_basename: detachable STRING tmp_basename: detachable STRING
-- Basename of tmp file -- Basename of tmp file
@@ -237,7 +244,7 @@ feature -- Implementation
feature -- Basic operation feature -- Basic operation
move_to (a_destination: STRING): BOOLEAN move_to (a_destination: READABLE_STRING_GENERAL): BOOLEAN
-- Move current uploaded file to `a_destination' -- Move current uploaded file to `a_destination'
--| Violates CQS principle. --| Violates CQS principle.
require require
@@ -246,10 +253,10 @@ feature -- Basic operation
local local
f: RAW_FILE f: RAW_FILE
do do
if attached tmp_name as n then if attached tmp_path as p then
create f.make (n) create f.make_with_path (p)
if f.exists then if f.exists then
f.change_name (a_destination) f.rename_file (a_destination)
Result := True Result := True
end end
end end
@@ -274,8 +281,8 @@ feature -- Status
local local
f: PLAIN_TEXT_FILE f: PLAIN_TEXT_FILE
do do
if attached tmp_name as n then if attached tmp_path as p then
create f.make (n) create f.make_with_path (p)
Result := f.exists Result := f.exists
end end
end end
@@ -288,10 +295,19 @@ feature -- Element change
error := e error := e
end end
set_tmp_path (p: like tmp_path)
do
tmp_path := p
end
set_tmp_name (n: like tmp_name) set_tmp_name (n: like tmp_name)
-- Set `tmp_name' to `n' -- Set `tmp_name' to `n'
do do
tmp_name := n if n /= Void then
set_tmp_path (create {PATH}.make_from_string (n))
else
set_tmp_path (Void)
end
end end
set_tmp_basename (n: like tmp_basename) set_tmp_basename (n: like tmp_basename)

View File

@@ -9,6 +9,13 @@ deferred class
inherit inherit
DEBUG_OUTPUT DEBUG_OUTPUT
SHARED_WSF_PERCENT_ENCODER
rename
percent_encoder as url_encoder
export
{NONE} all
end
feature -- Access feature -- Access
name: READABLE_STRING_32 name: READABLE_STRING_32
@@ -91,23 +98,26 @@ feature -- Helper
feature -- Status report feature -- Status report
debug_output: STRING debug_output: STRING_32
-- String that should be displayed in debugger to represent `Current'. -- String that should be displayed in debugger to represent `Current'.
do do
create Result.make_from_string (url_encoder.encoded_string (name) + "=" + url_encoder.encoded_string (string_representation)) create Result.make_from_string (name + {STRING_32} "=" + string_representation)
end end
feature {NONE} -- Implementation feature {NONE} -- Implementation
url_decoded_string (s: READABLE_STRING_8): READABLE_STRING_32 url_encoded_string (s: READABLE_STRING_GENERAL): STRING_8
-- Decoded url-encoded string `s' -- Decoded url-encoded string `s'
do do
Result := url_encoder.decoded_string (s) create Result.make (s.count)
url_encoder.append_percent_encoded_string_to (s, Result)
end end
url_encoder: URL_ENCODER url_decoded_string (s: READABLE_STRING_GENERAL): STRING_32
once -- Decoded url-encoded string `s'
create {UTF8_URL_ENCODER} Result --| Chrome is UTF-8 encoding the non ascii in query do
create Result.make (s.count)
url_encoder.append_percent_decoded_string_to (s, Result)
end end
feature -- Visitor feature -- Visitor
@@ -117,7 +127,7 @@ feature -- Visitor
end end
note note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -10,6 +10,8 @@ class
inherit inherit
WSF_RESPONSE_MESSAGE WSF_RESPONSE_MESSAGE
SHARED_UTF8_URL_ENCODER
create create
make, make,
make_with_content_type, make_with_content_type,
@@ -17,26 +19,26 @@ create
feature {NONE} -- Initialization feature {NONE} -- Initialization
make (a_file_name: READABLE_STRING_8) make (a_file_name: READABLE_STRING_GENERAL)
do do
set_status_code ({HTTP_STATUS_CODE}.ok) set_status_code ({HTTP_STATUS_CODE}.ok)
file_name := a_file_name create file_path.make_from_string (a_file_name)
base_name := basename (a_file_name) base_name := basename (file_path)
get_content_type get_content_type
initialize initialize
end end
make_with_content_type (a_content_type: READABLE_STRING_8; a_filename: READABLE_STRING_8) make_with_content_type (a_content_type: READABLE_STRING_8; a_filename: READABLE_STRING_GENERAL)
-- Initialize `Current'. -- Initialize `Current'.
do do
set_status_code ({HTTP_STATUS_CODE}.ok) set_status_code ({HTTP_STATUS_CODE}.ok)
file_name := a_filename create file_path.make_from_string (a_filename)
base_name := basename (a_filename) base_name := basename (file_path)
content_type := a_content_type content_type := a_content_type
initialize initialize
end end
make_html (a_filename: READABLE_STRING_8) make_html (a_filename: READABLE_STRING_GENERAL)
-- Initialize `Current'. -- Initialize `Current'.
do do
make_with_content_type ({HTTP_MIME_TYPES}.text_html, a_filename) make_with_content_type ({HTTP_MIME_TYPES}.text_html, a_filename)
@@ -45,15 +47,14 @@ feature {NONE} -- Initialization
initialize initialize
local local
h: like header h: like header
d: HTTP_DATE
do do
create h.make create h.make
header := h header := h
h.put_content_type (content_type) h.put_content_type (content_type)
h.put_transfer_encoding_binary h.put_transfer_encoding_binary
h.put_content_length (filesize (file_name)) h.put_content_length (filesize (file_path))
h.put_content_disposition ("attachment", "filename=%""+ base_name +"%"") h.put_content_disposition ("attachment", "filename=%""+ base_name +"%"")
if attached filedate (file_name) as dt then if attached filedate (file_path) as dt then
h.put_last_modified (dt) h.put_last_modified (dt)
end end
end end
@@ -89,7 +90,14 @@ feature -- Access
status_code: INTEGER assign set_status_code status_code: INTEGER assign set_status_code
file_path: PATH
file_name: READABLE_STRING_8 file_name: READABLE_STRING_8
obsolete
"Use `file_path.name' for unicode support [2013-may]"
do
Result := file_path.utf_8_name
end
base_name: READABLE_STRING_8 base_name: READABLE_STRING_8
@@ -125,73 +133,57 @@ feature {WSF_RESPONSE} -- Output
res.set_status_code (status_code) res.set_status_code (status_code)
res.put_header_text (header.string) res.put_header_text (header.string)
if not answer_head_request_method then if not answer_head_request_method then
send_file_content_to (file_name, res) send_file_content_to (file_path, res)
end end
end end
feature {NONE} -- Implementation: file system helper feature {NONE} -- Implementation: file system helper
filesize (fn: STRING): INTEGER filesize (fn: PATH): INTEGER
-- Size of the file `fn'. -- Size of the file `fn'.
local local
f: RAW_FILE f: RAW_FILE
do do
create f.make (fn) create f.make_with_path (fn)
if f.exists then if f.exists then
Result := f.count Result := f.count
end end
end end
filedate (fn: STRING): detachable DATE_TIME filedate (fn: PATH): detachable DATE_TIME
-- Size of the file `fn'. -- Size of the file `fn'.
local local
f: RAW_FILE f: RAW_FILE
d: HTTP_DATE d: HTTP_DATE
do do
create f.make (fn) create f.make_with_path (fn)
if f.exists then if f.exists then
create d.make_from_timestamp (f.date) create d.make_from_timestamp (f.date)
Result := d.date_time Result := d.date_time
end end
end end
file_extension (fn: STRING): STRING file_extension (fn: PATH): STRING_32
-- Extension of file `fn'. -- Extension of file `fn'.
local
p: INTEGER
do do
p := fn.last_index_of ('.', fn.count) if attached fn.extension as ext then
if p > 0 then Result := ext
Result := fn.substring (p + 1, fn.count)
else else
create Result.make_empty create Result.make_empty
end end
end end
basename (fn: STRING): STRING basename (fn: PATH): STRING
-- Basename of `fn'. -- Basename of `fn'.
local local
p: INTEGER s: READABLE_STRING_32
do do
p := fn.last_index_of ((create {OPERATING_ENVIRONMENT}).Directory_separator, fn.count) if attached fn.entry as p then
if p > 0 then s := p.name
Result := fn.substring (p + 1, fn.count)
else else
Result := fn s := fn.name
end
end
dirname (fn: STRING): STRING
-- Dirname of `fn'.
local
p: INTEGER
do
p := fn.last_index_of ((create {OPERATING_ENVIRONMENT}).Directory_separator, fn.count)
if p > 0 then
Result := fn.substring (1, p - 1)
else
create Result.make_empty
end end
Result := url_encoder.encoded_string (s)
end end
feature -- Content-type related feature -- Content-type related
@@ -203,7 +195,7 @@ feature -- Content-type related
m: detachable READABLE_STRING_8 m: detachable READABLE_STRING_8
do do
create m_map.make_default create m_map.make_default
m := m_map.mime_type (file_extension (file_name).as_lower) m := m_map.mime_type (file_extension (file_path).as_lower)
if m = Void then if m = Void then
m := {HTTP_MIME_TYPES}.application_force_download m := {HTTP_MIME_TYPES}.application_force_download
end end
@@ -212,15 +204,15 @@ feature -- Content-type related
feature -- Implementation: output feature -- Implementation: output
send_file_content_to (fn: READABLE_STRING_8; res: WSF_RESPONSE) send_file_content_to (fn: PATH; res: WSF_RESPONSE)
-- Send the content of file `fn' -- Send the content of file `fn'
require require
string_not_empty: not fn.is_empty string_not_empty: not fn.is_empty
is_readable: (create {RAW_FILE}.make (fn)).is_readable is_readable: (create {RAW_FILE}.make_with_path (fn)).is_readable
local local
f: RAW_FILE f: RAW_FILE
do do
create f.make (fn) create f.make_with_path (fn)
check f.exists and then f.is_readable end check f.exists and then f.is_readable end
f.open_read f.open_read

View File

@@ -11,30 +11,49 @@ inherit
WSF_RESPONSE_MESSAGE WSF_RESPONSE_MESSAGE
create create
make_with_path,
make_with_content_type_and_path,
make_html_with_path,
make, make,
make_with_content_type, make_with_content_type,
make_html make_html
feature {NONE} -- Initialization feature {NONE} -- Initialization
make (a_file_name: READABLE_STRING_8) make_with_path (a_path: PATH)
do do
set_status_code ({HTTP_STATUS_CODE}.ok) set_status_code ({HTTP_STATUS_CODE}.ok)
file_name := a_file_name file_path := a_path
get_content_type get_content_type
initialize initialize
end end
make_with_content_type (a_content_type: READABLE_STRING_8; a_filename: READABLE_STRING_8) make_with_content_type_and_path (a_content_type: READABLE_STRING_8; a_path: PATH)
-- Initialize `Current'.
do do
set_status_code ({HTTP_STATUS_CODE}.ok) set_status_code ({HTTP_STATUS_CODE}.ok)
file_name := a_filename file_path := a_path
content_type := a_content_type content_type := a_content_type
initialize initialize
end end
make_html (a_filename: READABLE_STRING_8) make_html_with_path (a_path: PATH)
-- Initialize `Current'.
do
make_with_content_type_and_path ({HTTP_MIME_TYPES}.text_html, a_path)
end
make (a_file_name: READABLE_STRING_GENERAL)
do
make_with_path (create {PATH}.make_from_string (a_file_name))
end
make_with_content_type (a_content_type: READABLE_STRING_8; a_file_name: READABLE_STRING_GENERAL)
-- Initialize `Current'.
do
make_with_content_type_and_path (a_content_type, create {PATH}.make_from_string (a_file_name))
end
make_html (a_filename: READABLE_STRING_GENERAL)
-- Initialize `Current'. -- Initialize `Current'.
do do
make_with_content_type ({HTTP_MIME_TYPES}.text_html, a_filename) make_with_content_type ({HTTP_MIME_TYPES}.text_html, a_filename)
@@ -118,13 +137,21 @@ feature -- Access
content_type: READABLE_STRING_8 content_type: READABLE_STRING_8
-- Content-Type of the response -- Content-Type of the response
file_path: path
-- File path
file_name: READABLE_STRING_8 file_name: READABLE_STRING_8
obsolete
"Use `file_path.name' for unicode support [2013-may]"
do
Result := file_path.utf_8_name
end
file_exists: BOOLEAN file_exists: BOOLEAN
-- File exists? -- File exists?
file_size: INTEGER file_size: INTEGER
-- Size of file named `file_name' -- Size of file `file_path'
head, bottom: detachable READABLE_STRING_8 head, bottom: detachable READABLE_STRING_8
-- Eventual head and bottom part -- Eventual head and bottom part
@@ -184,7 +211,7 @@ feature {WSF_RESPONSE} -- Output
res.put_string (s) res.put_string (s)
end end
if not answer_head_request_method then if not answer_head_request_method then
send_file_content_to (file_name, res) send_file_content_to (file_path, res)
end end
s := bottom s := bottom
if s /= Void then if s /= Void then
@@ -200,40 +227,37 @@ feature {NONE} -- Implementation: file system helper
local local
f: RAW_FILE f: RAW_FILE
do do
create f.make (file_name) create f.make_with_path (file_path)
file_exists := f.exists file_exists := f.exists
end end
get_file_size get_file_size
-- Get `file_size' from file named `file_name' -- Get `file_size' from file named `file_path'
require require
file_exists: file_exists file_exists: file_exists
local local
f: RAW_FILE f: RAW_FILE
do do
create f.make (file_name) create f.make_with_path (file_path)
file_size := f.count file_size := f.count
end end
file_last_modified: detachable DATE_TIME file_last_modified: detachable DATE_TIME
-- Get `file_size' from file named `file_name' -- Get `file_size' from file named `file_path'
require require
file_exists: file_exists file_exists: file_exists
local local
f: RAW_FILE f: RAW_FILE
do do
create f.make (file_name) create f.make_with_path (file_path)
create Result.make_from_epoch (f.change_date) create Result.make_from_epoch (f.change_date)
end end
file_extension (fn: STRING): STRING file_extension (fn: PATH): STRING_32
-- Extension of file `fn'. -- Extension of file `fn'.
local
p: INTEGER
do do
p := fn.last_index_of ('.', fn.count) if attached fn.extension as ext then
if p > 0 then Result := ext
Result := fn.substring (p + 1, fn.count)
else else
create Result.make_empty create Result.make_empty
end end
@@ -242,13 +266,13 @@ feature {NONE} -- Implementation: file system helper
feature -- Content-type related feature -- Content-type related
get_content_type get_content_type
-- Content type associated with `file_name' -- Content type associated with `file_path'
local local
m_map: HTTP_FILE_EXTENSION_MIME_MAPPING m_map: HTTP_FILE_EXTENSION_MIME_MAPPING
m: detachable READABLE_STRING_8 m: detachable READABLE_STRING_8
do do
create m_map.make_default create m_map.make_default
m := m_map.mime_type (file_extension (file_name).as_lower) m := m_map.mime_type (file_extension (file_path).as_lower)
if m = Void then if m = Void then
m := {HTTP_MIME_TYPES}.application_force_download m := {HTTP_MIME_TYPES}.application_force_download
end end
@@ -257,16 +281,16 @@ feature -- Content-type related
feature {NONE} -- Implementation: output feature {NONE} -- Implementation: output
send_file_content_to (fn: READABLE_STRING_8; res: WSF_RESPONSE) send_file_content_to (fn: PATH; res: WSF_RESPONSE)
-- Send the content of file `fn' -- Send the content of file `fn'
require require
string_not_empty: not fn.is_empty string_not_empty: not fn.is_empty
is_readable: (create {RAW_FILE}.make (fn)).is_readable is_readable: (create {RAW_FILE}.make_with_path (fn)).is_readable
file_exists: file_exists file_exists: file_exists
local local
f: RAW_FILE f: RAW_FILE
do do
create f.make (fn) create f.make_with_path (fn)
check f.is_readable end check f.is_readable end
f.open_read f.open_read

View File

@@ -15,7 +15,7 @@ class
WSF_SERVICE_LAUNCHER_OPTIONS WSF_SERVICE_LAUNCHER_OPTIONS
inherit inherit
ANY TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL]
redefine redefine
default_create default_create
end end
@@ -23,7 +23,8 @@ inherit
create create
default_create, default_create,
make, make,
make_from_array make_from_array,
make_from_iterable
convert convert
make_from_array ({ARRAY [TUPLE [name: READABLE_STRING_GENERAL; value: detachable ANY]]}) make_from_array ({ARRAY [TUPLE [name: READABLE_STRING_GENERAL; value: detachable ANY]]})
@@ -44,6 +45,19 @@ feature {NONE} -- Initialization
make_from_array (a_options: ARRAY [TUPLE [name: READABLE_STRING_GENERAL; value: detachable ANY]]) make_from_array (a_options: ARRAY [TUPLE [name: READABLE_STRING_GENERAL; value: detachable ANY]])
do do
make make
append_array_of_options (a_options)
end
make_from_iterable (a_options: TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL])
do
make
append_options (a_options)
end
feature -- Merging
append_array_of_options (a_options: ARRAY [TUPLE [name: READABLE_STRING_GENERAL; value: detachable ANY]])
do
across across
a_options as opt a_options as opt
loop loop
@@ -53,6 +67,15 @@ feature {NONE} -- Initialization
end end
end end
append_options (a_options: TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL])
do
across
a_options as o
loop
set_option (o.key, o.item)
end
end
feature -- Access feature -- Access
option (a_name: READABLE_STRING_GENERAL): detachable ANY option (a_name: READABLE_STRING_GENERAL): detachable ANY
@@ -60,6 +83,14 @@ feature -- Access
Result := options.item (a_name) Result := options.item (a_name)
end end
feature -- Access
new_cursor: TABLE_ITERATION_CURSOR [detachable ANY, READABLE_STRING_GENERAL]
-- Fresh cursor associated with current structure
do
Result := options.new_cursor
end
feature -- Element change feature -- Element change
set_option (a_name: READABLE_STRING_GENERAL; a_value: detachable ANY) set_option (a_name: READABLE_STRING_GENERAL; a_value: detachable ANY)
@@ -75,13 +106,13 @@ feature -- Element change
feature {NONE} -- Implementation feature {NONE} -- Implementation
options: HASH_TABLE [detachable ANY, READABLE_STRING_GENERAL] options: STRING_TABLE [detachable ANY]
-- Custom options which might be support (or not) by the default service -- Custom options which might be support (or not) by the default service
invariant invariant
options_attached: options /= Void options_attached: options /= Void
note note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -11,28 +11,40 @@ inherit
WSF_SERVICE_LAUNCHER_OPTIONS WSF_SERVICE_LAUNCHER_OPTIONS
create create
make_from_file make_from_file,
make_from_file_and_defaults
feature {NONE} -- Initialization feature {NONE} -- Initialization
make_from_file (a_filename: READABLE_STRING_32) make_from_file (a_filename: READABLE_STRING_GENERAL)
-- Initialize `Current'. -- Initialize `Current'.
do do
make make
import (a_filename) import (a_filename)
end end
make_from_file_and_defaults (a_filename: READABLE_STRING_GENERAL; dft: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
-- Initialize `Current'.
do
make
if dft /= Void then
append_options (dft)
end
import (a_filename)
end
feature {NONE} -- Implementation feature {NONE} -- Implementation
import (a_filename: READABLE_STRING_32) import (a_filename: READABLE_STRING_GENERAL)
-- Import ini file content -- Import ini file content
local local
f: PLAIN_TEXT_FILE f: PLAIN_TEXT_FILE
l,v: STRING_8 l,v: STRING_8
p: INTEGER p: INTEGER
do do
--FIXME: handle unicode filename here. create f.make_with_name (a_filename)
create f.make (a_filename)
if f.exists and f.is_readable then if f.exists and f.is_readable then
f.open_read f.open_read
from from
@@ -60,7 +72,7 @@ feature {NONE} -- Implementation
end end
note note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -26,6 +26,18 @@ class
inherit inherit
DEBUG_OUTPUT DEBUG_OUTPUT
SHARED_EXECUTION_ENVIRONMENT
export
{NONE} all
end
SHARED_WSF_PERCENT_ENCODER
rename
percent_encoder as url_encoder
export
{NONE} all
end
create {WSF_TO_WGI_SERVICE} create {WSF_TO_WGI_SERVICE}
make_from_wgi make_from_wgi
@@ -40,26 +52,25 @@ feature {NONE} -- Initialization
do do
wgi_request := r wgi_request := r
create string_equality_tester
if attached r.meta_variables as l_vars then if attached r.meta_variables as l_vars then
create tb.make_with_key_tester (l_vars.count, string_equality_tester) create tb.make_equal (l_vars.count)
across across
l_vars as c l_vars as c
loop loop
tb.force (new_string_value (c.key, c.item), c.key) if attached {READABLE_STRING_8} c.key as s8 then
tb.force (new_string_value (s8, c.item), c.key)
else
tb.force (new_string_value (url_encoded_string (c.key), c.item), c.key)
end
end end
else else
create tb.make_with_key_tester (0, string_equality_tester) create tb.make_equal (0)
end end
meta_variables_table := tb meta_variables_table := tb
meta_variables := tb
create error_handler.make create error_handler.make
create uploaded_files_table.make_with_key_tester (0, string_equality_tester) create uploaded_files_table.make_equal (0)
set_raw_input_data_recorded (False) set_raw_input_data_recorded (False)
create {IMMUTABLE_STRING_32} empty_string.make_empty create execution_variables_table.make_equal (0)
create execution_variables_table.make_with_key_tester (0, string_equality_tester)
execution_variables_table.compare_objects
initialize initialize
analyze analyze
@@ -96,12 +107,13 @@ feature {NONE} -- Initialization
request_method := req.request_method request_method := req.request_method
--| PATH_INFO --| PATH_INFO
path_info := raw_url_encoder.decoded_string (req.path_info) percent_encoded_path_info := req.path_info
path_info := url_decoded_string (req.path_info)
--| PATH_TRANSLATED --| PATH_TRANSLATED
s8 := req.path_translated s8 := req.path_translated
if s8 /= Void then if s8 /= Void then
path_translated := raw_url_encoder.decoded_string (s8) path_translated := url_decoded_string (s8)
end end
--| Here one can set its own environment entries if needed --| Here one can set its own environment entries if needed
@@ -111,6 +123,7 @@ feature {NONE} -- Initialization
end end
wgi_request: WGI_REQUEST wgi_request: WGI_REQUEST
-- Associated WGI request
feature -- Destroy feature -- Destroy
@@ -125,6 +138,26 @@ feature -- Destroy
loop loop
delete_uploaded_file (c.item) delete_uploaded_file (c.item)
end end
content_length_value := 0
content_type := Void
execution_variables_table.wipe_out
internal_cookies_table := Void
internal_form_data_parameters_table := Void
internal_query_parameters_table := Void
internal_server_url := Void
internal_url_base := Void
form_parameters_table.wipe_out
mime_handlers := Void
path_info := empty_string_32
path_parameters_source := Void
path_parameters_table := Void
path_translated := Void
raw_input_data := Void
raw_input_data_recorded := False
request_method := empty_string_8
set_uploaded_file_path (Void)
-- wgi_request
end end
feature -- Status report feature -- Status report
@@ -353,12 +386,12 @@ feature {WSF_REQUEST_EXPORTER} -- Override value
feature {NONE} -- Access: global variable feature {NONE} -- Access: global variable
items_table: HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL] items_table: STRING_TABLE [WSF_VALUE]
-- Table containing all the various variables -- Table containing all the various variables
-- Warning: this is computed each time, if you change the content of other containers -- Warning: this is computed each time, if you change the content of other containers
-- this won't update this Result's content, unless you query it again -- this won't update this Result's content, unless you query it again
do do
create Result.make_with_key_tester (20, string_equality_tester) create Result.make_equal (20)
if attached path_parameters as l_path_parameters then if attached path_parameters as l_path_parameters then
across across
@@ -558,7 +591,7 @@ feature -- Execution variables
feature {NONE} -- Execution variables: implementation feature {NONE} -- Execution variables: implementation
execution_variables_table: HASH_TABLE_EX [detachable ANY, READABLE_STRING_GENERAL] execution_variables_table: STRING_TABLE [detachable ANY]
feature -- Access: CGI Meta variables feature -- Access: CGI Meta variables
@@ -582,6 +615,9 @@ feature -- Access: CGI Meta variables
meta_variables: ITERABLE [WSF_STRING] meta_variables: ITERABLE [WSF_STRING]
-- CGI meta variables values -- CGI meta variables values
do
Result := meta_variables_table
end
meta_string_variable_or_default (a_name: READABLE_STRING_GENERAL; a_default: READABLE_STRING_32; use_default_when_empty: BOOLEAN): READABLE_STRING_32 meta_string_variable_or_default (a_name: READABLE_STRING_GENERAL; a_default: READABLE_STRING_32; use_default_when_empty: BOOLEAN): READABLE_STRING_32
-- Value for meta parameter `a_name' -- Value for meta parameter `a_name'
@@ -617,7 +653,7 @@ feature -- Access: CGI Meta variables
feature {NONE} -- Access: CGI meta parameters feature {NONE} -- Access: CGI meta parameters
meta_variables_table: HASH_TABLE_EX [WSF_STRING, READABLE_STRING_GENERAL] meta_variables_table: STRING_TABLE [WSF_STRING]
-- CGI Environment parameters -- CGI Environment parameters
feature -- Access: CGI meta parameters - 1.1 feature -- Access: CGI meta parameters - 1.1
@@ -739,6 +775,11 @@ feature -- Access: CGI meta parameters - 1.1
Result := wgi_request.gateway_interface Result := wgi_request.gateway_interface
end end
percent_encoded_path_info: READABLE_STRING_8
-- Non decoded PATH_INFO value from CGI.
-- See `path_info' for the related percent decoded value.
--| This value should be used by component dealing only with ASCII path
path_info: READABLE_STRING_32 path_info: READABLE_STRING_32
-- The PATH_INFO metavariable specifies a path to be interpreted -- The PATH_INFO metavariable specifies a path to be interpreted
-- by the CGI script. It identifies the resource or sub-resource -- by the CGI script. It identifies the resource or sub-resource
@@ -767,6 +808,8 @@ feature -- Access: CGI meta parameters - 1.1
-- The PATH_INFO value is case-sensitive, and the server MUST -- The PATH_INFO value is case-sensitive, and the server MUST
-- preserve the case of the PATH_INFO element of the URI when -- preserve the case of the PATH_INFO element of the URI when
-- making it available to scripts. -- making it available to scripts.
--
-- See `percent_encoded_path_info' to get the original non decoded path info.
path_translated: detachable READABLE_STRING_32 path_translated: detachable READABLE_STRING_32
-- PATH_TRANSLATED is derived by taking any path-info component -- PATH_TRANSLATED is derived by taking any path-info component
@@ -1150,7 +1193,7 @@ feature -- Cookies
feature {NONE} -- Cookies feature {NONE} -- Cookies
cookies_table: HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL] cookies_table: STRING_TABLE [WSF_VALUE]
-- Expanded cookies variable -- Expanded cookies variable
local local
i,j,p,n: INTEGER i,j,p,n: INTEGER
@@ -1161,8 +1204,7 @@ feature {NONE} -- Cookies
if l_cookies = Void then if l_cookies = Void then
if attached {WSF_STRING} meta_variable ({WSF_META_NAMES}.http_cookie) as val then if attached {WSF_STRING} meta_variable ({WSF_META_NAMES}.http_cookie) as val then
s := val.value s := val.value
create l_cookies.make_with_key_tester (5, string_equality_tester) create l_cookies.make_equal (5)
l_cookies.compare_objects
from from
n := s.count n := s.count
p := 1 p := 1
@@ -1190,8 +1232,7 @@ feature {NONE} -- Cookies
end end
end end
else else
create l_cookies.make_with_key_tester (0, string_equality_tester) create l_cookies.make_equal (0)
l_cookies.compare_objects
end end
internal_cookies_table := l_cookies internal_cookies_table := l_cookies
end end
@@ -1217,7 +1258,7 @@ feature -- Path parameters
feature {NONE} -- Query parameters: implementation feature {NONE} -- Query parameters: implementation
path_parameters_table: detachable HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL] path_parameters_table: detachable STRING_TABLE [WSF_VALUE]
-- Parameters computed from `path_parameters_source' -- Parameters computed from `path_parameters_source'
--| most often coming from the associated route from WSF_ROUTER --| most often coming from the associated route from WSF_ROUTER
@@ -1240,8 +1281,7 @@ feature {WSF_REQUEST_PATH_PARAMETERS_SOURCE} -- Path parameters: Element change
if l_count = 0 then if l_count = 0 then
l_table := Void l_table := Void
else else
create l_table.make_with_key_tester (l_count, string_equality_tester) create l_table.make_equal (l_count)
l_table.compare_objects
if attached src.path_parameters as tb then if attached src.path_parameters as tb then
across across
tb as c tb as c
@@ -1278,7 +1318,7 @@ feature -- Query parameters
feature {NONE} -- Query parameters: implementation feature {NONE} -- Query parameters: implementation
query_parameters_table: HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL] query_parameters_table: STRING_TABLE [WSF_VALUE]
-- Parameters extracted from QUERY_STRING -- Parameters extracted from QUERY_STRING
local local
vars: like internal_query_parameters_table vars: like internal_query_parameters_table
@@ -1303,13 +1343,12 @@ feature {NONE} -- Query parameters: implementation
end end
end end
vars := urlencoded_parameters (s) vars := urlencoded_parameters (s)
vars.compare_objects
internal_query_parameters_table := vars internal_query_parameters_table := vars
end end
Result := vars Result := vars
end end
urlencoded_parameters (a_content: detachable READABLE_STRING_8): HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL] urlencoded_parameters (a_content: detachable READABLE_STRING_8): STRING_TABLE [WSF_VALUE]
-- Import `a_content' -- Import `a_content'
local local
n, p, i, j: INTEGER n, p, i, j: INTEGER
@@ -1317,13 +1356,13 @@ feature {NONE} -- Query parameters: implementation
l_name, l_value: READABLE_STRING_8 l_name, l_value: READABLE_STRING_8
do do
if a_content = Void then if a_content = Void then
create Result.make_with_key_tester (0, string_equality_tester) create Result.make_equal (0)
else else
n := a_content.count n := a_content.count
if n = 0 then if n = 0 then
create Result.make_with_key_tester (0, string_equality_tester) create Result.make_equal (0)
else else
create Result.make_with_key_tester (3, string_equality_tester) --| 3 = arbitrary value create Result.make_equal (3) --| 3 = arbitrary value
from from
p := 1 p := 1
until until
@@ -1348,6 +1387,8 @@ feature {NONE} -- Query parameters: implementation
end end
end end
end end
ensure
result_with_object_comparison: Result.object_comparison
end end
feature -- Form fields and related feature -- Form fields and related
@@ -1452,7 +1493,7 @@ feature {NONE} -- Implementation: MIME handler
feature {NONE} -- Form fields and related feature {NONE} -- Form fields and related
uploaded_files_table: HASH_TABLE_EX [WSF_UPLOADED_FILE, READABLE_STRING_GENERAL] uploaded_files_table: STRING_TABLE [WSF_UPLOADED_FILE]
get_form_parameters get_form_parameters
-- Variables sent by POST, ... request -- Variables sent by POST, ... request
@@ -1464,14 +1505,12 @@ feature {NONE} -- Form fields and related
vars := internal_form_data_parameters_table vars := internal_form_data_parameters_table
if vars = Void then if vars = Void then
if not is_chunked_input and content_length_value = 0 then if not is_chunked_input and content_length_value = 0 then
create vars.make_with_key_tester (0, string_equality_tester) create vars.make_equal (0)
vars.compare_objects
else else
if raw_input_data_recorded then if raw_input_data_recorded then
create l_raw_data_cell.put (Void) create l_raw_data_cell.put (Void)
end end
create vars.make_with_key_tester (5, string_equality_tester) create vars.make_equal (5)
vars.compare_objects
l_type := content_type l_type := content_type
if l_type /= Void and then attached mime_handler (l_type) as hdl then if l_type /= Void and then attached mime_handler (l_type) as hdl then
@@ -1488,7 +1527,7 @@ feature {NONE} -- Form fields and related
internal_form_data_parameters_table /= Void internal_form_data_parameters_table /= Void
end end
form_parameters_table: HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL] form_parameters_table: STRING_TABLE [WSF_VALUE]
-- Variables sent by POST request -- Variables sent by POST request
local local
vars: like internal_form_data_parameters_table vars: like internal_form_data_parameters_table
@@ -1497,14 +1536,14 @@ feature {NONE} -- Form fields and related
vars := internal_form_data_parameters_table vars := internal_form_data_parameters_table
if vars = Void then if vars = Void then
check form_parameters_already_retrieved: False end check form_parameters_already_retrieved: False end
create vars.make_with_key_tester (0, string_equality_tester) create vars.make_equal (0)
end end
Result := vars Result := vars
end end
feature {NONE} -- Implementation: smart parameter identification feature {NONE} -- Implementation: smart parameter identification
add_value_to_table (a_name: READABLE_STRING_8; a_value: READABLE_STRING_8; a_table: HASH_TABLE [WSF_VALUE, READABLE_STRING_GENERAL]) add_value_to_table (a_name: READABLE_STRING_8; a_value: READABLE_STRING_8; a_table: STRING_TABLE [WSF_VALUE])
-- Add urlencoded parameter `a_name'=`a_value' to `a_table' -- Add urlencoded parameter `a_name'=`a_value' to `a_table'
-- following smart computation such as handling the "..[..]" as table -- following smart computation such as handling the "..[..]" as table
local local
@@ -1548,7 +1587,7 @@ feature {NONE} -- Implementation: smart parameter identification
if p > 0 then if p > 0 then
q := r.index_of ({CHARACTER_8} ']', p + 1) q := r.index_of ({CHARACTER_8} ']', p + 1)
if q > p then if q > p then
k32 := url_encoder.decoded_string (k) k32 := url_decoded_string (k)
if attached {WSF_TABLE} ptb.value (k32) as l_tb_value then if attached {WSF_TABLE} ptb.value (k32) as l_tb_value then
tb := l_tb_value tb := l_tb_value
else else
@@ -1610,7 +1649,7 @@ feature -- Uploaded File Handling
until until
l_files.after or Result l_files.after or Result
loop loop
if attached l_files.item_for_iteration.tmp_name as l_tmp_name and then l_tmp_name.same_string_general (a_filename) then if attached l_files.item_for_iteration.tmp_path as l_tmp_path and then a_filename.same_string (l_tmp_path.name) then
Result := True Result := True
end end
l_files.forth l_files.forth
@@ -1686,7 +1725,7 @@ feature -- URL Utility
elseif spos > 0 then elseif spos > 0 then
i := spos i := spos
end end
spos := l_rq_uri.substring_index (path_info, i) spos := l_rq_uri.substring_index (percent_encoded_path_info, i)
if spos > 0 then if spos > 0 then
l_base_url := l_rq_uri.substring (1, spos - 1) l_base_url := l_rq_uri.substring (1, spos - 1)
else else
@@ -1736,18 +1775,18 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling
f: RAW_FILE f: RAW_FILE
do do
if uploaded_files_table.has_item (uf) then if uploaded_files_table.has_item (uf) then
if attached uf.tmp_name as fn then if attached uf.tmp_path as fn then
create f.make (fn) create f.make_with_path (fn)
if f.exists and then f.is_writable then if f.exists and then f.is_writable then
f.delete f.delete
else else
error_handler.add_custom_error (0, "Can not delete uploaded file", "Can not delete file %""+ fn +"%"") error_handler.add_custom_error (0, "Can not delete uploaded file", {STRING_32} "Can not delete file %""+ fn.name + {STRING_32} "%"")
end end
else else
error_handler.add_custom_error (0, "Can not delete uploaded file", "Can not delete uploaded file %""+ uf.name +"%" Tmp File not found") error_handler.add_custom_error (0, "Can not delete uploaded file", {STRING_32} "Can not delete uploaded file %""+ uf.name + {STRING_32} "%" Tmp File not found")
end end
else else
error_handler.add_custom_error (0, "Not an uploaded file", "This file %""+ uf.name +"%" is not an uploaded file.") error_handler.add_custom_error (0, "Not an uploaded file", {STRING_32} "This file %""+ uf.name + {STRING_32} "%" is not an uploaded file.")
end end
end end
@@ -1757,8 +1796,8 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling
bn: STRING bn: STRING
l_safe_name: STRING l_safe_name: STRING
f: RAW_FILE f: RAW_FILE
dn: STRING dn: PATH
fn: FILE_NAME fn: PATH
d: DIRECTORY d: DIRECTORY
n: INTEGER n: INTEGER
rescued: BOOLEAN rescued: BOOLEAN
@@ -1768,30 +1807,28 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling
dn := p dn := p
else else
-- FIXME: should it be configured somewhere? -- FIXME: should it be configured somewhere?
dn := (create {EXECUTION_ENVIRONMENT}).current_working_directory dn := execution_environment.current_working_path
end end
create d.make (dn) create d.make_with_path (dn)
if d.exists and then d.is_writable then if d.exists and then d.is_writable then
l_safe_name := a_up_file.safe_filename l_safe_name := a_up_file.safe_filename
from from
create fn.make_from_string (dn) bn := "tmp-" + l_safe_name
bn := "EWF_tmp-" + l_safe_name fn := dn.extended (bn)
fn.set_file_name (bn) create f.make_with_path (fn)
create f.make (fn.string)
n := 0 n := 0
until until
not f.exists not f.exists
or else n > 1_000 or else n > 1_000
loop loop
n := n + 1 n := n + 1
fn.make_from_string (dn) bn := "tmp-" + n.out + "-" + l_safe_name
bn := "EWF_tmp-" + n.out + "-" + l_safe_name fn := dn.extended (bn)
fn.set_file_name (bn) f.make_with_path (fn)
f.make (fn.string)
end end
if not f.exists or else f.is_writable then if not f.exists or else f.is_writable then
a_up_file.set_tmp_name (f.name) a_up_file.set_tmp_path (f.path)
a_up_file.set_tmp_basename (bn) a_up_file.set_tmp_basename (bn)
f.open_write f.open_write
f.put_string (a_content) f.put_string (a_content)
@@ -1800,7 +1837,7 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling
a_up_file.set_error (-1) a_up_file.set_error (-1)
end end
else else
error_handler.add_custom_error (0, "Directory not writable", "Can not create file in directory %""+ dn +"%"") error_handler.add_custom_error (0, "Directory not writable", {STRING_32} "Can not create file in directory %""+ dn.name + {STRING_32} "%"")
end end
uploaded_files_table.force (a_up_file, a_up_file.name) uploaded_files_table.force (a_up_file, a_up_file.name)
else else
@@ -1813,13 +1850,13 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling
feature {WSF_REQUEST_EXPORTER} -- Settings feature {WSF_REQUEST_EXPORTER} -- Settings
uploaded_file_path: detachable READABLE_STRING_8 uploaded_file_path: detachable PATH
-- Optional folder path used to store uploaded files -- Optional folder path used to store uploaded files
set_uploaded_file_path (p: like uploaded_file_path) set_uploaded_file_path (p: like uploaded_file_path)
-- Set `uploaded_file_path' to `p'. -- Set `uploaded_file_path' to `p'.
require require
path_exists: p /= Void implies (create {DIRECTORY}.make (p)).exists path_exists: p /= Void implies (create {DIRECTORY}.make_with_path (p)).exists
do do
uploaded_file_path := p uploaded_file_path := p
end end
@@ -1874,8 +1911,6 @@ feature {NONE} -- Implementation
feature {NONE} -- Implementation: utilities feature {NONE} -- Implementation: utilities
string_equality_tester: STRING_EQUALITY_TESTER
single_slash_starting_string (s: READABLE_STRING_32): STRING_32 single_slash_starting_string (s: READABLE_STRING_32): STRING_32
-- Return the string `s' (or twin) with one and only one starting slash -- Return the string `s' (or twin) with one and only one starting slash
local local
@@ -1927,17 +1962,27 @@ feature {NONE} -- Implementation: utilities
create Result.make (a_name, a_value) create Result.make (a_name, a_value)
end end
empty_string: READABLE_STRING_32 empty_string_32: IMMUTABLE_STRING_32
-- Reusable empty string -- Reusable empty string
raw_url_encoder: URL_ENCODER
once once
create {URL_ENCODER} Result create Result.make_empty
end end
url_encoder: URL_ENCODER empty_string_8: IMMUTABLE_STRING_8
once once
create {UTF8_URL_ENCODER} Result create Result.make_empty
end
url_encoded_string (s: READABLE_STRING_GENERAL): STRING_8
do
create Result.make (s.count)
url_encoder.append_percent_encoded_string_to (s, Result)
end
url_decoded_string (s: READABLE_STRING_GENERAL): STRING_32
do
create Result.make (s.count)
url_encoder.append_percent_decoded_string_to (s, Result)
end end
date_time_utilities: HTTP_DATE_TIME_UTILITIES date_time_utilities: HTTP_DATE_TIME_UTILITIES
@@ -1947,7 +1992,8 @@ feature {NONE} -- Implementation: utilities
end end
invariant invariant
empty_string_unchanged: empty_string.is_empty empty_string_32_unchanged: empty_string_32.is_empty
empty_string_8_unchanged: empty_string_8.is_empty
wgi_request.content_type /= Void implies content_type /= Void wgi_request.content_type /= Void implies content_type /= Void
note note

View File

@@ -11,22 +11,10 @@
<assertions precondition="true"/> <assertions precondition="true"/>
</option> </option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="encoding" location="$ISE_LIBRARY\library\encoding\encoding-safe.ecf"/>
<cluster name="src" location="src\" recursive="true"> <cluster name="src" location="src\" recursive="true">
<file_rule> <file_rule>
<exclude>/tests$</exclude> <exclude>/tests$</exclude>
<exclude>/spec$</exclude>
</file_rule> </file_rule>
<cluster name="src_before_70" location="$|spec\before_70\" recursive="true">
<condition>
<version type="compiler" max="7.0.8.7585"/>
</condition>
</cluster>
<cluster name="src_70" location="$|spec\70\" recursive="true">
<condition>
<version type="compiler" min="7.0.8.7586"/>
</condition>
</cluster>
</cluster> </cluster>
</target> </target>
</system> </system>

View File

@@ -11,22 +11,10 @@
<assertions precondition="true"/> <assertions precondition="true"/>
</option> </option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="encoding" location="$ISE_LIBRARY\library\encoding\encoding.ecf"/>
<cluster name="src" location="src\" recursive="true"> <cluster name="src" location="src\" recursive="true">
<file_rule> <file_rule>
<exclude>/tests$</exclude> <exclude>/tests$</exclude>
<exclude>/spec$</exclude>
</file_rule> </file_rule>
<cluster name="src_before_70" location="$\spec\before_70" recursive="true">
<condition>
<version type="compiler" max="7.0.8.7585"/>
</condition>
</cluster>
<cluster name="src_70" location="$\spec\70" recursive="true">
<condition>
<version type="compiler" min="7.0.8.7586"/>
</condition>
</cluster>
</cluster> </cluster>
</target> </target>
</system> </system>

View File

@@ -1,26 +1,21 @@
note note
description : "Objects that ..." description: "Objects to access the shared once UTF8_URL_ENCODER ..."
author : "$Author$" date: "$Date$"
date : "$Date$" revision: "$Revision$"
revision : "$Revision$"
deferred class class
UTF8_ENCODER_HELPER SHARED_UTF8_URL_ENCODER
inherit feature -- Encoder
ANY
UNICODE_CONVERSION url_encoder: UTF8_URL_ENCODER
export -- Shared UTF8 URL encoder.
{NONE} all once
{ANY} is_valid_utf8 create Result
undefine
is_little_endian
end end
note note
copyright: "2011-2011, Eiffel Software and others" copyright: "2011-2012, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software
@@ -29,4 +24,5 @@ note
Website http://www.eiffel.com Website http://www.eiffel.com
Customer support http://support.eiffel.com Customer support http://support.eiffel.com
]" ]"
end end

View File

@@ -1,73 +0,0 @@
note
description : "Objects that ..."
author : "$Author$"
date : "$Date$"
revision : "$Revision$"
deferred class
UTF8_ENCODER_HELPER
inherit
ANY
UNICODE_CONVERSION
export
{NONE} all
undefine
is_little_endian
end
feature -- Status report
is_valid_utf8 (a_string: STRING): BOOLEAN
-- Is `a_string' valid UTF-8 string?
require
a_string_not_void: a_string /= Void
local
l_nat8: NATURAL_8
l_code: NATURAL_32
i, nb: INTEGER
do
from
i := 1
nb := a_string.count
Result := True
until
i > nb or not Result
loop
l_nat8 := a_string.code (i).to_natural_8
if l_nat8 <= 127 then
-- Form 0xxxxxxx.
elseif (l_nat8 & 0xE0) = 0xC0 then
-- Form 110xxxxx 10xxxxxx.
l_code := (l_nat8 & 0x1F).to_natural_32 |<< 6
i := i + 1
elseif (l_nat8 & 0xF0) = 0xE0 then
-- Form 1110xxxx 10xxxxxx 10xxxxxx.
i := i + 2
elseif (l_nat8 & 0xF8) = 0xF0 then
-- Form 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx.
i := i + 3
elseif (l_nat8 & 0xFC) = 0xF8 then
-- Starts with 111110xx
Result := False
else
-- Starts with 1111110x
Result := False
end
i := i + 1
end
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

View File

@@ -15,8 +15,6 @@ class
inherit inherit
ENCODER [READABLE_STRING_32, READABLE_STRING_8] ENCODER [READABLE_STRING_32, READABLE_STRING_8]
UTF8_ENCODER_HELPER
PLATFORM PLATFORM
export export
{NONE} all {NONE} all
@@ -37,9 +35,13 @@ feature -- Encoder
encoded_string (s: READABLE_STRING_32): STRING_8 encoded_string (s: READABLE_STRING_32): STRING_8
-- UTF8-encoded value of `s'. -- UTF8-encoded value of `s'.
do
Result := general_encoded_string (s)
end
general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8
do do
Result := utf32_to_utf8 (s) Result := utf32_to_utf8 (s)
has_error := not last_conversion_successful
end end
feature -- Decoder feature -- Decoder
@@ -48,11 +50,34 @@ feature -- Decoder
-- The UTF8-encoded equivalent of the given string -- The UTF8-encoded equivalent of the given string
do do
Result := utf8_to_utf32 (v) Result := utf8_to_utf32 (v)
has_error := not last_conversion_successful has_error := not is_valid_utf8 (v)
end
feature {NONE} -- UTF implementation
utf32_to_utf8 (s: READABLE_STRING_GENERAL): STRING_8
local
utf: UTF_CONVERTER
do
Result := utf.utf_32_string_to_utf_8_string_8 (s)
end
utf8_to_utf32 (s: READABLE_STRING_8): STRING_32
local
utf: UTF_CONVERTER
do
Result := utf.utf_8_string_8_to_string_32 (s)
end
is_valid_utf8 (s: READABLE_STRING_8): BOOLEAN
local
utf: UTF_CONVERTER
do
Result := utf.is_valid_utf_8_string_8 (s)
end end
note note
copyright: "2011-2012, Eiffel Software and others" copyright: "2011-2013, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -15,23 +15,24 @@ class
inherit inherit
URL_ENCODER URL_ENCODER
redefine redefine
default_create,
name, name,
general_encoded_string, general_encoded_string,
encoded_string, partial_encoded_string, encoded_string, partial_encoded_string,
decoded_string decoded_string
select
encoded_string,
decoded_string,
has_error
end end
UTF8_ENCODER_HELPER UTF8_ENCODER
rename
general_encoded_string as utf8_general_encoded_string,
encoded_string as utf8_encoded_string,
decoded_string as utf8_decoded_string,
has_error as utf8_has_error
redefine redefine
default_create name
end
feature {NONE} -- Initialization
default_create
do
Precursor {UTF8_ENCODER_HELPER}
end end
feature -- Access feature -- Access
@@ -46,27 +47,22 @@ feature -- Encoder
encoded_string (s: READABLE_STRING_32): STRING_8 encoded_string (s: READABLE_STRING_32): STRING_8
-- URL-encoded value of `s'. -- URL-encoded value of `s'.
do do
Result := utf32_to_utf8 (s) Result := general_encoded_string (s)
Result := Precursor (Result)
end end
general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8 general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8
do do
if attached {READABLE_STRING_32} s as s32 then Result := utf8_general_encoded_string (s)
Result := utf32_to_utf8 (s32) Result := Precursor {URL_ENCODER} (Result)
else has_error := has_error or utf8_has_error
Result := s.as_string_8
end
Result := Precursor (Result)
end end
partial_encoded_string (s: READABLE_STRING_GENERAL; a_ignore: ARRAY [CHARACTER]): STRING_8 partial_encoded_string (s: READABLE_STRING_GENERAL; a_ignore: ARRAY [CHARACTER]): STRING_8
-- URL-encoded value of `s'. -- URL-encoded value of `s'.
do do
Result := Precursor (s, a_ignore) Result := utf8_general_encoded_string (s)
if not has_error then Result := Precursor {URL_ENCODER} (Result, a_ignore)
Result := utf32_to_utf8 (Result) has_error := has_error or utf8_has_error
end
end end
feature -- Decoder feature -- Decoder
@@ -74,17 +70,15 @@ feature -- Decoder
decoded_string (v: READABLE_STRING_8): STRING_32 decoded_string (v: READABLE_STRING_8): STRING_32
-- The URL-encoded equivalent of the given string -- The URL-encoded equivalent of the given string
do do
Result := Precursor (v) Result := Precursor {URL_ENCODER} (v)
if not has_error then if not has_error then
if is_valid_utf8 (Result) then Result := utf8_decoded_string (Result)
Result := utf8_to_utf32 (Result) has_error := utf8_has_error
has_error := not last_conversion_successful
end
end end
end end
note note
copyright: "2011-2012, Eiffel Software and others" copyright: "2011-2013, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

BIN
tools/bin/ecf_updater.exe Normal file

Binary file not shown.