From 40c6aff423e79132391756de7199102b83819ce7 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 23 Mar 2012 16:40:13 +0100 Subject: [PATCH] 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 --- library/protocol/http/src/http_content_type.e | 318 ++++++++++++++++++ library/protocol/http/src/http_header.e | 77 ++++- .../null/src/wgi_null_file_input_stream.e | 89 +++++ .../null/src/wgi_null_input_stream.e | 21 +- .../null/src/wgi_null_string_input_stream.e | 86 +++++ ...pplication_x_www_form_urlencoded_handler.e | 6 +- .../server/wsf/src/mime/wsf_mime_handler.e | 4 +- .../mime/wsf_multipart_form_data_handler.e | 37 +- library/server/wsf/src/support/wsf_header.e | 8 +- library/server/wsf/src/wsf_request.e | 19 +- .../tests/src/test_wsf_request_mime_handler.e | 118 +++++++ .../tests/src/test_wsf_request_script_url.e | 2 +- .../server/wsf/tests/src/wgi_request_null.e | 27 +- library/server/wsf/tests/tests-safe.ecf | 1 + 14 files changed, 741 insertions(+), 72 deletions(-) create mode 100644 library/protocol/http/src/http_content_type.e create mode 100644 library/server/ewsgi/connectors/null/src/wgi_null_file_input_stream.e create mode 100644 library/server/ewsgi/connectors/null/src/wgi_null_string_input_stream.e create mode 100644 library/server/wsf/tests/src/test_wsf_request_mime_handler.e diff --git a/library/protocol/http/src/http_content_type.e b/library/protocol/http/src/http_content_type.e new file mode 100644 index 00000000..f8069818 --- /dev/null +++ b/library/protocol/http/src/http_content_type.e @@ -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 diff --git a/library/protocol/http/src/http_header.e b/library/protocol/http/src/http_header.e index c2f59d59..d78e63f3 100644 --- a/library/protocol/http/src/http_header.e +++ b/library/protocol/http/src/http_header.e @@ -28,7 +28,8 @@ create make, make_with_count, make_from_array, - make_from_header + make_from_header, + make_from_raw_header_data convert 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) end + make_from_raw_header_data (h: READABLE_STRING_8) + -- Create Current from raw header data + local + line : detachable STRING + lines: LIST [READABLE_STRING_8] + do + lines := h.split ('%N') + make_with_count (lines.count) + across + lines as c + loop + line := c.item + if not line.is_empty then + if line[line.count] = '%R' then + line.remove_tail (1) + end + add_header (line) + end + end + end + feature -- Recycle recycle @@ -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") end + to_name_value_iterable: ITERABLE [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]] + local + res: ARRAYED_LIST [TUPLE [READABLE_STRING_8, READABLE_STRING_8]] + do + create res.make (headers.count) + across + headers as c + loop + if attached header_name_value (c.item) as tu then + res.extend (tu) + end + end + Result := res + end + feature -- Header: filling append_array (a_headers: ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]]) @@ -141,7 +178,7 @@ feature -- Header change: general require h_not_empty: not h.is_empty do - force_header_by_name (header_name (h), h) + force_header_by_name (header_name_colon (h), h) end 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) -- Add header `h' or replace existing header of same header name `n' 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 l_headers: like headers do @@ -552,7 +589,7 @@ feature {NONE} -- Implementation: Header 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 --| ex: for "Foo-bar: something", this will return "Foo-bar:" local @@ -581,6 +618,36 @@ feature {NONE} -- Implementation: Header Result := s end + header_name_value (h: READABLE_STRING_8): detachable TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8] + -- If any, header's [name,value] + --| ex: for "Foo-bar: something", this will return ["Foo-bar", "something"] + local + s: detachable STRING_8 + i,n: INTEGER + c: CHARACTER + do + from + i := 1 + n := h.count + create s.make (10) + until + i > n or c = ':' or s = Void + loop + c := h[i] + inspect c + when ':' then + when '-', 'a' .. 'z', 'A' .. 'Z' then + s.extend (c) + else + s := Void + end + i := i + 1 + end + if s /= Void then + Result := [s, h.substring (i, n)] + end + end + feature {NONE} -- Implementation append_line_to (s: READABLE_STRING_8; h: STRING_8) @@ -595,7 +662,7 @@ feature {NONE} -- Implementation h.append_character ('%N') 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 do Result := dt.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT" diff --git a/library/server/ewsgi/connectors/null/src/wgi_null_file_input_stream.e b/library/server/ewsgi/connectors/null/src/wgi_null_file_input_stream.e new file mode 100644 index 00000000..6e0282f0 --- /dev/null +++ b/library/server/ewsgi/connectors/null/src/wgi_null_file_input_stream.e @@ -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 diff --git a/library/server/ewsgi/connectors/null/src/wgi_null_input_stream.e b/library/server/ewsgi/connectors/null/src/wgi_null_input_stream.e index 75fdbd0c..93461b24 100644 --- a/library/server/ewsgi/connectors/null/src/wgi_null_input_stream.e +++ b/library/server/ewsgi/connectors/null/src/wgi_null_input_stream.e @@ -5,31 +5,12 @@ note date: "$Date$" revision: "$Revision$" -class +deferred class WGI_NULL_INPUT_STREAM inherit 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 copyright: "2011-2011, Eiffel Software and others" diff --git a/library/server/ewsgi/connectors/null/src/wgi_null_string_input_stream.e b/library/server/ewsgi/connectors/null/src/wgi_null_string_input_stream.e new file mode 100644 index 00000000..09df2d2e --- /dev/null +++ b/library/server/ewsgi/connectors/null/src/wgi_null_string_input_stream.e @@ -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 diff --git a/library/server/wsf/src/mime/wsf_application_x_www_form_urlencoded_handler.e b/library/server/wsf/src/mime/wsf_application_x_www_form_urlencoded_handler.e index d8d9bcb8..325e1151 100644 --- a/library/server/wsf/src/mime/wsf_application_x_www_form_urlencoded_handler.e +++ b/library/server/wsf/src/mime/wsf_application_x_www_form_urlencoded_handler.e @@ -14,14 +14,14 @@ inherit feature -- Status report - valid_content_type (a_content_type: READABLE_STRING_8): BOOLEAN + valid_content_type (a_content_type: HTTP_CONTENT_TYPE): BOOLEAN 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 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]) local l_content: READABLE_STRING_8 diff --git a/library/server/wsf/src/mime/wsf_mime_handler.e b/library/server/wsf/src/mime/wsf_mime_handler.e index c1125f4a..af3b2470 100644 --- a/library/server/wsf/src/mime/wsf_mime_handler.e +++ b/library/server/wsf/src/mime/wsf_mime_handler.e @@ -9,13 +9,13 @@ deferred class feature -- Status report - valid_content_type (a_content_type: READABLE_STRING_8): BOOLEAN + valid_content_type (a_content_type: HTTP_CONTENT_TYPE): BOOLEAN deferred end 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]) -- 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' diff --git a/library/server/wsf/src/mime/wsf_multipart_form_data_handler.e b/library/server/wsf/src/mime/wsf_multipart_form_data_handler.e index 21a18dac..9861208f 100644 --- a/library/server/wsf/src/mime/wsf_multipart_form_data_handler.e +++ b/library/server/wsf/src/mime/wsf_multipart_form_data_handler.e @@ -17,33 +17,21 @@ create feature {NONE} -- Initialization - make (a_err_handler: like error_handler) + make -- Instantiate Current do - error_handler := a_err_handler 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 - valid_content_type (a_content_type: READABLE_STRING_8): BOOLEAN + valid_content_type (a_content_type: HTTP_CONTENT_TYPE): BOOLEAN 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 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]) local s: like full_input_data @@ -58,24 +46,23 @@ feature -- Execution 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 --| FIXME[2011-06-21]: integrate eMIME parser library require - t_attached: t /= Void + a_content_type_valid: a_content_type /= Void and not a_content_type.has_error s_attached: s /= Void vars_attached: vars /= Void local p,i,next_b: INTEGER l_boundary_prefix: STRING - l_boundary: STRING l_boundary_len: INTEGER + l_boundary: detachable READABLE_STRING_8 m: STRING is_crlf: BOOLEAN do - p := t.substring_index ("boundary=", 1) - if p > 0 then - l_boundary := t.substring (p + 9, t.count) + l_boundary := a_content_type.parameter ("boundary") + if l_boundary /= Void then p := s.substring_index (l_boundary, 1) if p > 1 then 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.right_adjust 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 i := next_b end @@ -235,10 +222,10 @@ feature {NONE} -- Implementation: Form analyzer add_string_value_to_table (l_name, l_content, vars) end else - error_handler.add_custom_error (0, "unamed multipart entry", Void) + req.error_handler.add_custom_error (0, "unamed multipart entry", Void) end else - error_handler.add_custom_error (0, "missformed multipart entry", Void) + req.error_handler.add_custom_error (0, "missformed multipart entry", Void) end end diff --git a/library/server/wsf/src/support/wsf_header.e b/library/server/wsf/src/support/wsf_header.e index d7336deb..41a96d06 100644 --- a/library/server/wsf/src/support/wsf_header.e +++ b/library/server/wsf/src/support/wsf_header.e @@ -22,15 +22,15 @@ note class WSF_HEADER -obsolete "Use HTTP_HEADER [2011-nov-25]" - inherit HTTP_HEADER create make, - make_with_count - + make_with_count, + make_from_array, + make_from_header, + make_from_raw_header_data note copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index b2b4a4f5..fe2be820 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -77,6 +77,14 @@ feature {NONE} -- Initialization content_length_value := 0 end + -- Content-Type + s8 := wgi_request.content_type + if s8 /= Void then + content_type := s8 + else + content_type := Void + end + --| 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 -- 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 -- the Internet Media Type [9] of the attached entity if the type -- 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 -- metavariable when communicating the wgi_request information to the -- script. - do - Result := wgi_request.content_type - end gateway_interface: READABLE_STRING_8 -- This metavariable is set to the dialect of CGI being used by @@ -1145,7 +1150,7 @@ feature -- Access: MIME handler hdls.force (a_handler) 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' do if attached mime_handlers as hdls then @@ -1169,7 +1174,7 @@ feature {NONE} -- Implementation: MIME handler init_mime_handlers 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}) end @@ -1566,7 +1571,7 @@ feature {NONE} -- Implementation: utilities invariant empty_string_unchanged: empty_string.is_empty - + wgi_request.content_type /= Void implies content_type /= Void note copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" diff --git a/library/server/wsf/tests/src/test_wsf_request_mime_handler.e b/library/server/wsf/tests/src/test_wsf_request_mime_handler.e new file mode 100644 index 00000000..f9a88668 --- /dev/null +++ b/library/server/wsf/tests/src/test_wsf_request_mime_handler.e @@ -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 + + diff --git a/library/server/wsf/tests/src/test_wsf_request_script_url.e b/library/server/wsf/tests/src/test_wsf_request_script_url.e index 11928265..5a97ca86 100644 --- a/library/server/wsf/tests/src/test_wsf_request_script_url.e +++ b/library/server/wsf/tests/src/test_wsf_request_script_url.e @@ -81,7 +81,7 @@ feature {NONE} -- Implementation local wgi_req: WGI_REQUEST 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) end diff --git a/library/server/wsf/tests/src/wgi_request_null.e b/library/server/wsf/tests/src/wgi_request_null.e index c823fc09..3313d87d 100644 --- a/library/server/wsf/tests/src/wgi_request_null.e +++ b/library/server/wsf/tests/src/wgi_request_null.e @@ -14,19 +14,20 @@ inherit end create - make + make_with_file, + make_with_body 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 ht: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] - i: WGI_NULL_INPUT_STREAM + i: WGI_NULL_FILE_INPUT_STREAM c: WGI_NULL_CONNECTOR do create c.make - create i.make - create ht.make (a_meta.count) + create i.make (f) + create ht.make (10) across a_meta as curs loop @@ -35,6 +36,22 @@ feature {NONE} -- Initialization wgi_request_from_table_make (ht, i, c) 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 copyright: "2011-2011, Eiffel Software and others" diff --git a/library/server/wsf/tests/tests-safe.ecf b/library/server/wsf/tests/tests-safe.ecf index df2c4883..2f52e285 100644 --- a/library/server/wsf/tests/tests-safe.ecf +++ b/library/server/wsf/tests/tests-safe.ecf @@ -18,6 +18,7 @@ +