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

@@ -26,6 +26,18 @@ class
inherit
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}
make_from_wgi
@@ -40,26 +52,25 @@ feature {NONE} -- Initialization
do
wgi_request := r
create string_equality_tester
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
l_vars as c
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
else
create tb.make_with_key_tester (0, string_equality_tester)
create tb.make_equal (0)
end
meta_variables_table := tb
meta_variables := tb
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)
create {IMMUTABLE_STRING_32} empty_string.make_empty
create execution_variables_table.make_with_key_tester (0, string_equality_tester)
execution_variables_table.compare_objects
create execution_variables_table.make_equal (0)
initialize
analyze
@@ -96,12 +107,13 @@ feature {NONE} -- Initialization
request_method := req.request_method
--| 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
s8 := req.path_translated
if s8 /= Void then
path_translated := raw_url_encoder.decoded_string (s8)
path_translated := url_decoded_string (s8)
end
--| Here one can set its own environment entries if needed
@@ -111,6 +123,7 @@ feature {NONE} -- Initialization
end
wgi_request: WGI_REQUEST
-- Associated WGI request
feature -- Destroy
@@ -125,6 +138,26 @@ feature -- Destroy
loop
delete_uploaded_file (c.item)
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
feature -- Status report
@@ -353,12 +386,12 @@ feature {WSF_REQUEST_EXPORTER} -- Override value
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
-- 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
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
across
@@ -558,7 +591,7 @@ feature -- Execution variables
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
@@ -582,6 +615,9 @@ feature -- Access: CGI Meta variables
meta_variables: ITERABLE [WSF_STRING]
-- 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
-- Value for meta parameter `a_name'
@@ -617,7 +653,7 @@ feature -- Access: CGI Meta variables
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
feature -- Access: CGI meta parameters - 1.1
@@ -739,6 +775,11 @@ feature -- Access: CGI meta parameters - 1.1
Result := wgi_request.gateway_interface
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
-- The PATH_INFO metavariable specifies a path to be interpreted
-- 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
-- preserve the case of the PATH_INFO element of the URI when
-- 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 is derived by taking any path-info component
@@ -1150,7 +1193,7 @@ feature -- Cookies
feature {NONE} -- Cookies
cookies_table: HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL]
cookies_table: STRING_TABLE [WSF_VALUE]
-- Expanded cookies variable
local
i,j,p,n: INTEGER
@@ -1161,8 +1204,7 @@ feature {NONE} -- Cookies
if l_cookies = Void then
if attached {WSF_STRING} meta_variable ({WSF_META_NAMES}.http_cookie) as val then
s := val.value
create l_cookies.make_with_key_tester (5, string_equality_tester)
l_cookies.compare_objects
create l_cookies.make_equal (5)
from
n := s.count
p := 1
@@ -1190,8 +1232,7 @@ feature {NONE} -- Cookies
end
end
else
create l_cookies.make_with_key_tester (0, string_equality_tester)
l_cookies.compare_objects
create l_cookies.make_equal (0)
end
internal_cookies_table := l_cookies
end
@@ -1217,7 +1258,7 @@ feature -- Path parameters
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'
--| 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
l_table := Void
else
create l_table.make_with_key_tester (l_count, string_equality_tester)
l_table.compare_objects
create l_table.make_equal (l_count)
if attached src.path_parameters as tb then
across
tb as c
@@ -1278,7 +1318,7 @@ feature -- Query parameters
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
local
vars: like internal_query_parameters_table
@@ -1303,13 +1343,12 @@ feature {NONE} -- Query parameters: implementation
end
end
vars := urlencoded_parameters (s)
vars.compare_objects
internal_query_parameters_table := vars
end
Result := vars
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'
local
n, p, i, j: INTEGER
@@ -1317,13 +1356,13 @@ feature {NONE} -- Query parameters: implementation
l_name, l_value: READABLE_STRING_8
do
if a_content = Void then
create Result.make_with_key_tester (0, string_equality_tester)
create Result.make_equal (0)
else
n := a_content.count
if n = 0 then
create Result.make_with_key_tester (0, string_equality_tester)
create Result.make_equal (0)
else
create Result.make_with_key_tester (3, string_equality_tester) --| 3 = arbitrary value
create Result.make_equal (3) --| 3 = arbitrary value
from
p := 1
until
@@ -1348,6 +1387,8 @@ feature {NONE} -- Query parameters: implementation
end
end
end
ensure
result_with_object_comparison: Result.object_comparison
end
feature -- Form fields and related
@@ -1452,7 +1493,7 @@ feature {NONE} -- Implementation: MIME handler
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
-- Variables sent by POST, ... request
@@ -1464,14 +1505,12 @@ feature {NONE} -- Form fields and related
vars := internal_form_data_parameters_table
if vars = Void then
if not is_chunked_input and content_length_value = 0 then
create vars.make_with_key_tester (0, string_equality_tester)
vars.compare_objects
create vars.make_equal (0)
else
if raw_input_data_recorded then
create l_raw_data_cell.put (Void)
end
create vars.make_with_key_tester (5, string_equality_tester)
vars.compare_objects
create vars.make_equal (5)
l_type := content_type
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
end
form_parameters_table: HASH_TABLE_EX [WSF_VALUE, READABLE_STRING_GENERAL]
form_parameters_table: STRING_TABLE [WSF_VALUE]
-- Variables sent by POST request
local
vars: like internal_form_data_parameters_table
@@ -1497,14 +1536,14 @@ feature {NONE} -- Form fields and related
vars := internal_form_data_parameters_table
if vars = Void then
check form_parameters_already_retrieved: False end
create vars.make_with_key_tester (0, string_equality_tester)
create vars.make_equal (0)
end
Result := vars
end
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'
-- following smart computation such as handling the "..[..]" as table
local
@@ -1548,7 +1587,7 @@ feature {NONE} -- Implementation: smart parameter identification
if p > 0 then
q := r.index_of ({CHARACTER_8} ']', p + 1)
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
tb := l_tb_value
else
@@ -1610,7 +1649,7 @@ feature -- Uploaded File Handling
until
l_files.after or Result
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
end
l_files.forth
@@ -1686,7 +1725,7 @@ feature -- URL Utility
elseif spos > 0 then
i := spos
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
l_base_url := l_rq_uri.substring (1, spos - 1)
else
@@ -1736,18 +1775,18 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling
f: RAW_FILE
do
if uploaded_files_table.has_item (uf) then
if attached uf.tmp_name as fn then
create f.make (fn)
if attached uf.tmp_path as fn then
create f.make_with_path (fn)
if f.exists and then f.is_writable then
f.delete
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
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
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
@@ -1757,8 +1796,8 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling
bn: STRING
l_safe_name: STRING
f: RAW_FILE
dn: STRING
fn: FILE_NAME
dn: PATH
fn: PATH
d: DIRECTORY
n: INTEGER
rescued: BOOLEAN
@@ -1768,30 +1807,28 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling
dn := p
else
-- FIXME: should it be configured somewhere?
dn := (create {EXECUTION_ENVIRONMENT}).current_working_directory
dn := execution_environment.current_working_path
end
create d.make (dn)
create d.make_with_path (dn)
if d.exists and then d.is_writable then
l_safe_name := a_up_file.safe_filename
from
create fn.make_from_string (dn)
bn := "EWF_tmp-" + l_safe_name
fn.set_file_name (bn)
create f.make (fn.string)
bn := "tmp-" + l_safe_name
fn := dn.extended (bn)
create f.make_with_path (fn)
n := 0
until
not f.exists
or else n > 1_000
loop
n := n + 1
fn.make_from_string (dn)
bn := "EWF_tmp-" + n.out + "-" + l_safe_name
fn.set_file_name (bn)
f.make (fn.string)
bn := "tmp-" + n.out + "-" + l_safe_name
fn := dn.extended (bn)
f.make_with_path (fn)
end
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)
f.open_write
f.put_string (a_content)
@@ -1800,7 +1837,7 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling
a_up_file.set_error (-1)
end
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
uploaded_files_table.force (a_up_file, a_up_file.name)
else
@@ -1813,13 +1850,13 @@ feature {WSF_MIME_HANDLER} -- Temporary File handling
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
set_uploaded_file_path (p: like uploaded_file_path)
-- Set `uploaded_file_path' to `p'.
require
path_exists: p /= Void implies (create {DIRECTORY}.make (p)).exists
path_exists: p /= Void implies (create {DIRECTORY}.make_with_path (p)).exists
do
uploaded_file_path := p
end
@@ -1874,8 +1911,6 @@ feature {NONE} -- Implementation
feature {NONE} -- Implementation: utilities
string_equality_tester: STRING_EQUALITY_TESTER
single_slash_starting_string (s: READABLE_STRING_32): STRING_32
-- Return the string `s' (or twin) with one and only one starting slash
local
@@ -1927,17 +1962,27 @@ feature {NONE} -- Implementation: utilities
create Result.make (a_name, a_value)
end
empty_string: READABLE_STRING_32
empty_string_32: IMMUTABLE_STRING_32
-- Reusable empty string
raw_url_encoder: URL_ENCODER
once
create {URL_ENCODER} Result
create Result.make_empty
end
url_encoder: URL_ENCODER
empty_string_8: IMMUTABLE_STRING_8
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
date_time_utilities: HTTP_DATE_TIME_UTILITIES
@@ -1947,7 +1992,8 @@ feature {NONE} -- Implementation: utilities
end
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
note