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

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