Added class HTTP_CONTENT_TYPE to help manipulation of Content-Type value

Now WSF_REQUEST return a HTTP_CONTENT_TYPE if available
Adapted WSF_MIME_HANDLER to use this new class
Added one manual autotest to test MIME handler
This commit is contained in:
Jocelyn Fiat
2012-03-23 16:40:13 +01:00
parent ac9cbb0bd2
commit 40c6aff423
14 changed files with 741 additions and 72 deletions

View File

@@ -0,0 +1,318 @@
note
description: "[
CGI Meta variable define the CONTENT_TYPE entity
This class is to represent it as an object
the Internet Media Type [9] of the attached entity if the type
was provided via a "Content-type" field in the wgi_request header,
or if the server can determine it in the absence of a supplied
"Content-type" field. The syntax is the same as for the HTTP
"Content-Type" header field.
CONTENT_TYPE = "" | media-type
media-type = type "/" subtype *( ";" parameter)
type = token
subtype = token
parameter = attribute "=" value
attribute = token
value = token | quoted-string
The type, subtype, and parameter attribute names are not
case-sensitive. Parameter values MAY be case sensitive. Media
types and their use in HTTP are described in section 3.7 of
the HTTP/1.1 specification [8].
Example:
application/x-www-form-urlencoded
application/x-www-form-urlencoded; charset=UTF8
]"
date: "$Date$"
revision: "$Revision$"
class
HTTP_CONTENT_TYPE
inherit
DEBUG_OUTPUT
create
make,
make_from_content_type_header
convert
make_from_content_type_header ({READABLE_STRING_8, STRING_8, IMMUTABLE_STRING_8}),
string: {READABLE_STRING_8}
feature {NONE} -- Initialization
make (a_type, a_subtype: READABLE_STRING_8)
-- Create Current based on `a_type/a_subtype'
require
not a_type.is_empty
not a_subtype.is_empty
do
type := a_type
subtype := a_subtype
ensure
has_no_error: not has_error
end
make_from_content_type_header (s: READABLE_STRING_8)
-- Create Current from `s'
-- if `s' does not respect the expected syntax, has_error is True
local
t: STRING_8
i,n: INTEGER
p: INTEGER
pn,pv: STRING_8
do
-- Ignore starting space (should not be any)
from
i := 1
n := s.count
until
i > n or not s[i].is_space
loop
i := i + 1
end
if i < n then
-- Look for semi colon as parameter separation
p := s.index_of (';', i)
if p > 0 then
t := s.substring (i, p - 1)
-- Skip eventual space
i := p + 1
from
until
i > n or not s[i].is_space
loop
i := i + 1
end
if i < n then
p := s.index_of ('=', i)
if p > 0 then
pn := s.substring (i, p - 1)
pv := s.substring (p + 1, n)
pv.right_adjust
if pv.count > 0 and pv [1] = '%"' then
if pv [pv.count] = '%"' then
pv := pv.substring (2, pv.count - 1)
else
has_error := True
-- missing closing double quote.
end
end
if not has_error then
set_parameter (pn, pv)
end
else
-- expecting: attribute "=" value
has_error := True
end
end
else
t := s.substring (i, n)
end
-- Remove eventual trailing space
t.right_adjust
-- Extract type and subtype
p := t.index_of ('/', 1)
if p = 0 then
has_error := True
type := t
subtype := ""
else
subtype := t.substring (p + 1, t.count)
type := t
t.keep_head (p - 1)
end
else
has_error := True
type := ""
subtype := type
end
ensure
not has_error implies (create {HTTP_CONTENT_TYPE}.make_from_content_type_header (string)).same_string (string)
end
feature -- Access
has_error: BOOLEAN
-- Current has error?
--| Mainly in relation with `make_from_content_type_header'
type: READABLE_STRING_8
-- Main type
subtype: READABLE_STRING_8
-- Sub type
has_parameter: BOOLEAN
-- Has Current a parameter?
do
Result := parameter_information /= Void
end
parameter (a_name: READABLE_STRING_8): detachable READABLE_STRING_8
-- Value for eventual parameter named `a_name'.
do
if has_parameter and then parameter_name.same_string (a_name) then
Result := parameter_value
end
end
parameter_name: READABLE_STRING_8
-- Parameter's name if any.
require
has_parameter: has_parameter
do
if attached parameter_information as l_info then
Result := l_info.name
else
Result := ""
check has_parameter: False end
end
end
parameter_value: READABLE_STRING_8
-- Parameter's value if any.
require
has_parameter: has_parameter
do
if attached parameter_information as l_info then
Result := l_info.value
else
Result := ""
check has_parameter: False end
end
end
feature -- Conversion
string: READABLE_STRING_8
-- String representation of type/subtype; attribute=value
local
res: like internal_string
do
res := internal_string
if res = Void then
create res.make_from_string (type_and_subtype_string)
if has_parameter then
res.append_character (';')
res.append_character (' ')
res.append (parameter_name)
res.append_character ('=')
res.append_character ('%"')
res.append (parameter_value)
res.append_character ('%"')
end
internal_string := res
end
Result := res
end
type_and_subtype_string: READABLE_STRING_8
-- String representation of type/subtype
local
res: like internal_type_and_subtype_string
s: like subtype
do
res := internal_type_and_subtype_string
if res = Void then
create res.make_from_string (type)
s := subtype
if not s.is_empty then
check has_error: has_error end
-- Just in case not is_valid, we keep in `type' the original string
res.append_character ('/')
res.append (s)
end
internal_type_and_subtype_string := res
end
Result := res
end
feature {NONE} -- Internal
internal_string: detachable STRING_8
internal_type_and_subtype_string: detachable STRING_8
feature -- Status report
same_as (other: HTTP_CONTENT_TYPE): BOOLEAN
do
Result := other.type.same_string (other.type) and then
other.subtype.same_string (other.subtype)
if Result then
Result := has_parameter = other.has_parameter
if has_parameter then
Result := parameter_name.same_string (other.parameter_name) and then
parameter_value.same_string (other.parameter_value)
end
end
end
same_type_and_subtype (s: READABLE_STRING_8): BOOLEAN
do
Result := type_and_subtype_string.same_string (s)
end
same_string (s: READABLE_STRING_8): BOOLEAN
do
Result := string.same_string (s)
end
feature -- Element change
set_parameter (a_name: like parameter_name; a_value: like parameter_value)
-- Set parameter for `a_name' to `a_value'
do
parameter_information := [a_name, a_value]
internal_string := Void
end
remove_parameter
-- Remove parameter
do
parameter_information := Void
internal_string := Void
end
feature {NONE} -- Implementation
parameter_information: detachable TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]
feature -- Status report
debug_output: STRING
-- String that should be displayed in debugger to represent `Current'.
do
if type /= Void and subtype /= Void then
Result := type + "/" + subtype
if attached parameter_information as p_info then
Result.append ("; " + p_info.name + "=" + "%"" + p_info.value + "%"")
end
else
Result := ""
end
end
invariant
has_parameter implies parameter_name /= Void and parameter_value /= Void
type_and_subtype_not_empty: not has_error implies not type.is_empty and not subtype.is_empty
note
copyright: "2011-2012, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -28,7 +28,8 @@ create
make, make,
make_with_count, make_with_count,
make_from_array, make_from_array,
make_from_header make_from_header,
make_from_raw_header_data
convert convert
make_from_array ({ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]]}), make_from_array ({ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]]}),
@@ -66,6 +67,27 @@ feature {NONE} -- Initialization
append_header_object (a_header) append_header_object (a_header)
end end
make_from_raw_header_data (h: READABLE_STRING_8)
-- Create Current from raw header data
local
line : detachable STRING
lines: LIST [READABLE_STRING_8]
do
lines := h.split ('%N')
make_with_count (lines.count)
across
lines as c
loop
line := c.item
if not line.is_empty then
if line[line.count] = '%R' then
line.remove_tail (1)
end
add_header (line)
end
end
end
feature -- Recycle feature -- Recycle
recycle recycle
@@ -100,6 +122,21 @@ feature -- Access
result_has_single_ending_cr_lf: Result.count >= 4 implies not Result.substring (Result.count - 3, Result.count).same_string ("%R%N%R%N") result_has_single_ending_cr_lf: Result.count >= 4 implies not Result.substring (Result.count - 3, Result.count).same_string ("%R%N%R%N")
end end
to_name_value_iterable: ITERABLE [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]
local
res: ARRAYED_LIST [TUPLE [READABLE_STRING_8, READABLE_STRING_8]]
do
create res.make (headers.count)
across
headers as c
loop
if attached header_name_value (c.item) as tu then
res.extend (tu)
end
end
Result := res
end
feature -- Header: filling feature -- Header: filling
append_array (a_headers: ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]]) append_array (a_headers: ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]])
@@ -141,7 +178,7 @@ feature -- Header change: general
require require
h_not_empty: not h.is_empty h_not_empty: not h.is_empty
do do
force_header_by_name (header_name (h), h) force_header_by_name (header_name_colon (h), h)
end end
add_header_key_value (k,v: READABLE_STRING_8) add_header_key_value (k,v: READABLE_STRING_8)
@@ -529,7 +566,7 @@ feature {NONE} -- Implementation: Header
force_header_by_name (n: detachable READABLE_STRING_8; h: READABLE_STRING_8) force_header_by_name (n: detachable READABLE_STRING_8; h: READABLE_STRING_8)
-- Add header `h' or replace existing header of same header name `n' -- Add header `h' or replace existing header of same header name `n'
require require
h_has_name_n: (n /= Void and attached header_name (h) as hn) implies n.same_string (hn) h_has_name_n: (n /= Void and attached header_name_colon (h) as hn) implies n.same_string (hn)
local local
l_headers: like headers l_headers: like headers
do do
@@ -552,7 +589,7 @@ feature {NONE} -- Implementation: Header
end end
end end
header_name (h: READABLE_STRING_8): detachable READABLE_STRING_8 header_name_colon (h: READABLE_STRING_8): detachable STRING_8
-- If any, header's name with colon -- If any, header's name with colon
--| ex: for "Foo-bar: something", this will return "Foo-bar:" --| ex: for "Foo-bar: something", this will return "Foo-bar:"
local local
@@ -581,6 +618,36 @@ feature {NONE} -- Implementation: Header
Result := s Result := s
end end
header_name_value (h: READABLE_STRING_8): detachable TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]
-- If any, header's [name,value]
--| ex: for "Foo-bar: something", this will return ["Foo-bar", "something"]
local
s: detachable STRING_8
i,n: INTEGER
c: CHARACTER
do
from
i := 1
n := h.count
create s.make (10)
until
i > n or c = ':' or s = Void
loop
c := h[i]
inspect c
when ':' then
when '-', 'a' .. 'z', 'A' .. 'Z' then
s.extend (c)
else
s := Void
end
i := i + 1
end
if s /= Void then
Result := [s, h.substring (i, n)]
end
end
feature {NONE} -- Implementation feature {NONE} -- Implementation
append_line_to (s: READABLE_STRING_8; h: STRING_8) append_line_to (s: READABLE_STRING_8; h: STRING_8)
@@ -595,7 +662,7 @@ feature {NONE} -- Implementation
h.append_character ('%N') h.append_character ('%N')
end end
date_to_rfc1123_http_date_format (dt: DATE_TIME): READABLE_STRING_8 date_to_rfc1123_http_date_format (dt: DATE_TIME): STRING_8
-- String representation of `dt' using the RFC 1123 -- String representation of `dt' using the RFC 1123
do do
Result := dt.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT" Result := dt.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT"

View File

@@ -0,0 +1,89 @@
note
description: "Summary description for WGI_NULL_FILE_INPUT_STREAM."
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
class
WGI_NULL_FILE_INPUT_STREAM
inherit
WGI_NULL_INPUT_STREAM
create
make
feature {NONE} -- Initialization
make (f: FILE)
do
file := f
end
file: FILE
feature -- Input
read_character
-- Read the next character in input stream.
-- Make the result available in `last_character'
do
file.read_character
end
read_string (nb: INTEGER)
-- Read the next `nb' characters and
-- make the string result available in `last_string'
do
file.read_stream (nb)
end
feature -- Access
last_string: STRING_8
-- Last string read.
--
-- Note: this query *might* return the same object.
-- Therefore a clone should be used if the result
-- is to be kept beyond the next call to this feature.
-- However `last_string' is not shared between file objects.)
do
Result := file.last_string
end
last_character: CHARACTER_8
-- Last item read.
do
Result := file.last_character
end
feature -- Status report
is_open_read: BOOLEAN
-- Can items be read from input stream?
do
Result := file.is_open_read
end
end_of_input: BOOLEAN
-- Has the end of input stream been reached?
do
Result := file.end_of_file
end
invariant
note
copyright: "2011-2012, 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

@@ -5,31 +5,12 @@ note
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
class deferred class
WGI_NULL_INPUT_STREAM WGI_NULL_INPUT_STREAM
inherit inherit
WGI_INPUT_STREAM WGI_INPUT_STREAM
undefine
read_to_string
end
CONSOLE
rename
make as console_make,
read_stream as read_string,
end_of_file as end_of_input
end
create
make
feature {NONE} -- Initialization
make
do
make_open_stdin ("stdin")
end
note note
copyright: "2011-2011, Eiffel Software and others" copyright: "2011-2011, Eiffel Software and others"

View File

@@ -0,0 +1,86 @@
note
description: "Summary description for WGI_NULL_STRING_INPUT_STREAM."
legal: "See notice at end of class."
status: "See notice at end of class."
date: "$Date$"
revision: "$Revision$"
class
WGI_NULL_STRING_INPUT_STREAM
inherit
WGI_NULL_INPUT_STREAM
create
make
feature {NONE} -- Initialization
make (s: READABLE_STRING_8)
do
body := s
index := 1
count := s.count
last_string := ""
end
body: READABLE_STRING_8
index: INTEGER
count: INTEGER
feature -- Input
read_character
-- Read the next character in input stream.
-- Make the result available in `last_character'
do
last_character := body[index]
index := index + 1
end
read_string (nb: INTEGER)
-- Read the next `nb' characters and
-- make the string result available in `last_string'
local
e: INTEGER
do
e := (index + nb).min (count)
last_string := body.substring (index, e)
index := e + 1
end
feature -- Access
last_string: STRING_8
last_character: CHARACTER_8
feature -- Status report
is_open_read: BOOLEAN
-- Can items be read from input stream?
do
Result := True
end
end_of_input: BOOLEAN
-- Has the end of input stream been reached?
do
Result := index > count
end
invariant
note
copyright: "2011-2012, 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

@@ -14,14 +14,14 @@ inherit
feature -- Status report feature -- Status report
valid_content_type (a_content_type: READABLE_STRING_8): BOOLEAN valid_content_type (a_content_type: HTTP_CONTENT_TYPE): BOOLEAN
do do
Result := a_content_type.same_string ({HTTP_MIME_TYPES}.application_x_www_form_encoded) Result := a_content_type.same_type_and_subtype ({HTTP_MIME_TYPES}.application_x_www_form_encoded)
end end
feature -- Execution feature -- Execution
handle (a_content_type: READABLE_STRING_8; req: WSF_REQUEST; handle (a_content_type: HTTP_CONTENT_TYPE; req: WSF_REQUEST;
a_vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_32]; a_raw_data: detachable CELL [detachable STRING_8]) a_vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_32]; a_raw_data: detachable CELL [detachable STRING_8])
local local
l_content: READABLE_STRING_8 l_content: READABLE_STRING_8

View File

@@ -9,13 +9,13 @@ deferred class
feature -- Status report feature -- Status report
valid_content_type (a_content_type: READABLE_STRING_8): BOOLEAN valid_content_type (a_content_type: HTTP_CONTENT_TYPE): BOOLEAN
deferred deferred
end end
feature -- Execution feature -- Execution
handle (a_content_type: READABLE_STRING_8; req: WSF_REQUEST; handle (a_content_type: HTTP_CONTENT_TYPE; req: WSF_REQUEST;
a_vars: TABLE [WSF_VALUE, READABLE_STRING_32]; a_raw_data: detachable CELL [detachable STRING_8]) a_vars: TABLE [WSF_VALUE, READABLE_STRING_32]; a_raw_data: detachable CELL [detachable STRING_8])
-- Handle MIME content from request `req', eventually fill the `a_vars' (not yet available from `req') -- Handle MIME content from request `req', eventually fill the `a_vars' (not yet available from `req')
-- and if `a_raw_data' is attached, store any read data inside `a_raw_data' -- and if `a_raw_data' is attached, store any read data inside `a_raw_data'

View File

@@ -17,33 +17,21 @@ create
feature {NONE} -- Initialization feature {NONE} -- Initialization
make (a_err_handler: like error_handler) make
-- Instantiate Current -- Instantiate Current
do do
error_handler := a_err_handler
end end
feature -- Error handling
has_error: BOOLEAN
do
Result := error_handler.has_error
end
error_handler: ERROR_HANDLER
-- Error handler
-- By default initialized to new handler
feature -- Status report feature -- Status report
valid_content_type (a_content_type: READABLE_STRING_8): BOOLEAN valid_content_type (a_content_type: HTTP_CONTENT_TYPE): BOOLEAN
do do
Result := a_content_type.starts_with ({HTTP_MIME_TYPES}.multipart_form_data) Result := a_content_type.same_type_and_subtype ({HTTP_MIME_TYPES}.multipart_form_data)
end end
feature -- Execution feature -- Execution
handle (a_content_type: READABLE_STRING_8; req: WSF_REQUEST; handle (a_content_type: HTTP_CONTENT_TYPE; req: WSF_REQUEST;
a_vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_32]; a_raw_data: detachable CELL [detachable STRING_8]) a_vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_32]; a_raw_data: detachable CELL [detachable STRING_8])
local local
s: like full_input_data s: like full_input_data
@@ -58,24 +46,23 @@ feature -- Execution
feature {NONE} -- Implementation: Form analyzer feature {NONE} -- Implementation: Form analyzer
analyze_multipart_form (req: WSF_REQUEST; t: READABLE_STRING_8; s: READABLE_STRING_8; vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_32]) analyze_multipart_form (req: WSF_REQUEST; a_content_type: HTTP_CONTENT_TYPE; s: READABLE_STRING_8; vars: HASH_TABLE [WSF_VALUE, READABLE_STRING_32])
-- Analyze multipart form content -- Analyze multipart form content
--| FIXME[2011-06-21]: integrate eMIME parser library --| FIXME[2011-06-21]: integrate eMIME parser library
require require
t_attached: t /= Void a_content_type_valid: a_content_type /= Void and not a_content_type.has_error
s_attached: s /= Void s_attached: s /= Void
vars_attached: vars /= Void vars_attached: vars /= Void
local local
p,i,next_b: INTEGER p,i,next_b: INTEGER
l_boundary_prefix: STRING l_boundary_prefix: STRING
l_boundary: STRING
l_boundary_len: INTEGER l_boundary_len: INTEGER
l_boundary: detachable READABLE_STRING_8
m: STRING m: STRING
is_crlf: BOOLEAN is_crlf: BOOLEAN
do do
p := t.substring_index ("boundary=", 1) l_boundary := a_content_type.parameter ("boundary")
if p > 0 then if l_boundary /= Void then
l_boundary := t.substring (p + 9, t.count)
p := s.substring_index (l_boundary, 1) p := s.substring_index (l_boundary, 1)
if p > 1 then if p > 1 then
l_boundary_prefix := s.substring (1, p - 1) l_boundary_prefix := s.substring (1, p - 1)
@@ -116,7 +103,7 @@ feature {NONE} -- Implementation: Form analyzer
m := s.substring (i - 1, s.count) m := s.substring (i - 1, s.count)
m.right_adjust m.right_adjust
if not l_boundary_prefix.same_string (m) then if not l_boundary_prefix.same_string (m) then
error_handler.add_custom_error (0, "Invalid form data", "Invalid ending for form data from input") req.error_handler.add_custom_error (0, "Invalid form data", "Invalid ending for form data from input")
end end
i := next_b i := next_b
end end
@@ -235,10 +222,10 @@ feature {NONE} -- Implementation: Form analyzer
add_string_value_to_table (l_name, l_content, vars) add_string_value_to_table (l_name, l_content, vars)
end end
else else
error_handler.add_custom_error (0, "unamed multipart entry", Void) req.error_handler.add_custom_error (0, "unamed multipart entry", Void)
end end
else else
error_handler.add_custom_error (0, "missformed multipart entry", Void) req.error_handler.add_custom_error (0, "missformed multipart entry", Void)
end end
end end

View File

@@ -22,15 +22,15 @@ note
class class
WSF_HEADER WSF_HEADER
obsolete "Use HTTP_HEADER [2011-nov-25]"
inherit inherit
HTTP_HEADER HTTP_HEADER
create create
make, make,
make_with_count make_with_count,
make_from_array,
make_from_header,
make_from_raw_header_data
note note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"

View File

@@ -77,6 +77,14 @@ feature {NONE} -- Initialization
content_length_value := 0 content_length_value := 0
end end
-- Content-Type
s8 := wgi_request.content_type
if s8 /= Void then
content_type := s8
else
content_type := Void
end
--| PATH_INFO --| PATH_INFO
path_info := url_encoder.decoded_string (wgi_request.path_info) path_info := url_encoder.decoded_string (wgi_request.path_info)
@@ -396,7 +404,7 @@ feature -- Access: CGI meta parameters - 1.1
content_length_value: NATURAL_64 content_length_value: NATURAL_64
-- Integer value related to `content_length" -- Integer value related to `content_length"
content_type: detachable READABLE_STRING_8 content_type: detachable HTTP_CONTENT_TYPE
-- If the wgi_request includes a message-body, CONTENT_TYPE is set to -- If the wgi_request includes a message-body, CONTENT_TYPE is set to
-- the Internet Media Type [9] of the attached entity if the type -- the Internet Media Type [9] of the attached entity if the type
-- was provided via a "Content-type" field in the wgi_request header, -- was provided via a "Content-type" field in the wgi_request header,
@@ -436,9 +444,6 @@ feature -- Access: CGI meta parameters - 1.1
-- determine the correct datatype, or it MAY omit this -- determine the correct datatype, or it MAY omit this
-- metavariable when communicating the wgi_request information to the -- metavariable when communicating the wgi_request information to the
-- script. -- script.
do
Result := wgi_request.content_type
end
gateway_interface: READABLE_STRING_8 gateway_interface: READABLE_STRING_8
-- This metavariable is set to the dialect of CGI being used by -- This metavariable is set to the dialect of CGI being used by
@@ -1145,7 +1150,7 @@ feature -- Access: MIME handler
hdls.force (a_handler) hdls.force (a_handler)
end end
mime_handler (a_content_type: READABLE_STRING_8): detachable WSF_MIME_HANDLER mime_handler (a_content_type: HTTP_CONTENT_TYPE): detachable WSF_MIME_HANDLER
-- Mime handler associated with `a_content_type' -- Mime handler associated with `a_content_type'
do do
if attached mime_handlers as hdls then if attached mime_handlers as hdls then
@@ -1169,7 +1174,7 @@ feature {NONE} -- Implementation: MIME handler
init_mime_handlers init_mime_handlers
do do
register_mime_handler (create {WSF_MULTIPART_FORM_DATA_HANDLER}.make (error_handler)) register_mime_handler (create {WSF_MULTIPART_FORM_DATA_HANDLER}.make)
register_mime_handler (create {WSF_APPLICATION_X_WWW_FORM_URLENCODED_HANDLER}) register_mime_handler (create {WSF_APPLICATION_X_WWW_FORM_URLENCODED_HANDLER})
end end
@@ -1566,7 +1571,7 @@ feature {NONE} -- Implementation: utilities
invariant invariant
empty_string_unchanged: empty_string.is_empty empty_string_unchanged: empty_string.is_empty
wgi_request.content_type /= Void implies content_type /= Void
note note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"

View File

@@ -0,0 +1,118 @@
note
description: "[
Eiffel tests that can be executed by testing tool.
]"
author: "EiffelStudio test wizard"
date: "$Date$"
revision: "$Revision$"
testing: "type/manual"
class
TEST_WSF_REQUEST_MIME_HANDLER
inherit
EQA_TEST_SET
WSF_SERVICE
undefine
default_create
end
feature {NONE} -- Events
port_number: INTEGER
base_url: detachable STRING
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
do
--| do nothing
end
feature -- Tests
test_mime_handler
local
req: WSF_REQUEST
b: STRING_8
h: WSF_HEADER
ct: HTTP_CONTENT_TYPE
m: ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]
do
m := <<
["REQUEST_METHOD", "GET"],
["QUERY_STRING", ""],
["REQUEST_URI", "/auto/test/foo"],
["SCRIPT_NAME", "/auto/test/test.ews"],
["PATH_INFO", "/foo"]
>>
create ct.make_from_content_type_header ({HTTP_MIME_TYPES}.multipart_form_data)
ct.set_parameter ("boundary", "__=_the_boundary_1332296477_1804289383_=__")
create h.make
h.put_content_type (ct)
b := "[
--__=_the_boundary_1332296477_1804289383_=__
Content-Disposition: form-data; name="user_name"
EWFdemo
--__=_the_boundary_1332296477_1804289383_=__
Content-Disposition: form-data; name="password"
EWFpassword
--__=_the_boundary_1332296477_1804289383_=__--
]"
h.put_content_length (b.count)
--| Case #1
req := new_request (m, h.string, b)
assert ("found user_name", attached req.form_parameter ("user_name") as u and then u.same_string ("EWFdemo"))
assert ("found password", attached req.form_parameter ("password") as u and then u.same_string ("EWFpassword"))
end
feature {NONE} -- Implementation
new_request (a_meta: ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]; h: READABLE_STRING_8; s: READABLE_STRING_8): WSF_REQUEST_NULL
local
wgi_req: WGI_REQUEST
l_header: WSF_HEADER
lst: ARRAYED_LIST [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]
vn: STRING_8
do
create lst.make (10)
across
a_meta as c
loop
lst.extend (c.item)
end
create l_header.make_from_raw_header_data (h)
across
l_header.to_name_value_iterable as c
loop
--| for Any Abc-Def-Ghi add (or replace) the HTTP_ABC_DEF_GHI variable to `env'
vn := c.item.name.as_upper
vn.replace_substring_all ("-", "_")
if
vn.starts_with ("CONTENT_") and then
(vn.same_string_general ({WGI_META_NAMES}.content_type) or vn.same_string_general ({WGI_META_NAMES}.content_length))
then
--| Keep this name
else
vn.prepend ("HTTP_")
end
lst.extend ([vn, c.item.value])
-- lst.extend (c.item)
end
create {WGI_REQUEST_NULL} wgi_req.make_with_body (lst, s)
create Result.make_from_wgi (wgi_req)
end
end

View File

@@ -81,7 +81,7 @@ feature {NONE} -- Implementation
local local
wgi_req: WGI_REQUEST wgi_req: WGI_REQUEST
do do
create {WGI_REQUEST_NULL} wgi_req.make (a_meta) create {WGI_REQUEST_NULL} wgi_req.make_with_file (a_meta, io.input)
create Result.make_from_wgi (wgi_req) create Result.make_from_wgi (wgi_req)
end end

View File

@@ -14,19 +14,20 @@ inherit
end end
create create
make make_with_file,
make_with_body
feature {NONE} -- Initialization feature {NONE} -- Initialization
make (a_meta: ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) make_with_file (a_meta: ITERABLE [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]; f: FILE)
local local
ht: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] ht: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
i: WGI_NULL_INPUT_STREAM i: WGI_NULL_FILE_INPUT_STREAM
c: WGI_NULL_CONNECTOR c: WGI_NULL_CONNECTOR
do do
create c.make create c.make
create i.make create i.make (f)
create ht.make (a_meta.count) create ht.make (10)
across across
a_meta as curs a_meta as curs
loop loop
@@ -35,6 +36,22 @@ feature {NONE} -- Initialization
wgi_request_from_table_make (ht, i, c) wgi_request_from_table_make (ht, i, c)
end end
make_with_body (a_meta: ITERABLE [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]; s: READABLE_STRING_8)
local
ht: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
i: WGI_NULL_STRING_INPUT_STREAM
c: WGI_NULL_CONNECTOR
do
create c.make
create i.make (s)
create ht.make (10)
across
a_meta as curs
loop
ht.force (curs.item.value, curs.item.name)
end
wgi_request_from_table_make (ht, i, c)
end
note note
copyright: "2011-2011, Eiffel Software and others" copyright: "2011-2011, Eiffel Software and others"

View File

@@ -18,6 +18,7 @@
<library name="http_client" location="..\..\..\client\http_client\http_client-safe.ecf" readonly="false"/> <library name="http_client" location="..\..\..\client\http_client\http_client-safe.ecf" readonly="false"/>
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/> <library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/> <library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
<library name="http" location="..\..\..\protocol\http\http-safe.ecf" readonly="false"/>
<library name="wsf" location="..\wsf-safe.ecf" readonly="false"/> <library name="wsf" location="..\wsf-safe.ecf" readonly="false"/>
<tests name="src" location=".\src\" recursive="true"/> <tests name="src" location=".\src\" recursive="true"/>
</target> </target>