diff --git a/library/network/http_client/src/http_client_request_context.e b/library/network/http_client/src/http_client_request_context.e index d8e7fe60..c2f069c3 100644 --- a/library/network/http_client/src/http_client_request_context.e +++ b/library/network/http_client/src/http_client_request_context.e @@ -58,11 +58,11 @@ feature -- Access -- Specific headers to use in addition to the one set in the related HTTP_CLIENT_SESSION --| note: the value from Current context override the one from the session in case of conflict - query_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32] + query_parameters: HTTP_CLIENT_REQUEST_QUERY_PARAMETERS -- Query parameters to be appended to the url --| note: if the url already contains a query_string, the `query_parameters' will be appended to the url - form_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32] + form_parameters: HTTP_CLIENT_REQUEST_FORM_PARAMETERS -- Form parameters upload_data: detachable READABLE_STRING_8 @@ -145,13 +145,25 @@ feature -- Element change add_query_parameter (k: READABLE_STRING_GENERAL; v: READABLE_STRING_GENERAL) -- Add a query parameter `k=v'. do - query_parameters.force (v.to_string_32, k.to_string_32) + query_parameters.force (create {HTTP_CLIENT_REQUEST_STRING_PARAMETER}.make (k, v)) end add_form_parameter (k: READABLE_STRING_GENERAL; v: READABLE_STRING_GENERAL) -- Add a form parameter `k'= `v'. do - form_parameters.force (v.to_string_32, k.to_string_32) + form_parameters.force (create {HTTP_CLIENT_REQUEST_STRING_PARAMETER}.make (k, v)) + end + + add_file_form_parameter (k: READABLE_STRING_GENERAL; a_location: READABLE_STRING_GENERAL; a_content_type: detachable READABLE_STRING_8) + -- Add a form file parameter named `k`, located at `a_location`, with optional content type `a_content_type`. + require + has_no_upload_data_or_filename: not has_upload_data and not has_upload_filename + local + param: HTTP_CLIENT_REQUEST_FILE_PARAMETER + do + create param.make_with_path (k, create {PATH}.make_from_string (a_location)) + param.set_content_type (a_content_type) + form_parameters.force (param) end set_credentials_required (b: BOOLEAN) @@ -164,7 +176,8 @@ feature -- Element change -- Set `upload_data' to `a_data' --| note: the Current context can have upload_data XOR upload_filename, but not both. require - has_upload_filename: (a_data /= Void and then not a_data.is_empty) implies not has_upload_filename + has_no_upload_filename: (a_data /= Void and then not a_data.is_empty) implies not has_upload_filename + has_no_form_data: (a_data /= Void and then not a_data.is_empty) implies not has_form_data do if a_data = Void or else a_data.is_empty then upload_data := Void @@ -180,6 +193,7 @@ feature -- Element change --| note: the Current context can have upload_data XOR upload_filename, but not both. require has_no_upload_data: (a_fn /= Void and then not a_fn.is_empty) implies not has_upload_data + has_no_form_data: (a_fn /= Void and then not a_fn.is_empty) implies not has_form_data do if a_fn = Void or else a_fn.is_empty then upload_filename := Void @@ -266,9 +280,9 @@ feature -- URL helpers a_url.append_character ('&') end l_first_param := False - uri_percent_encoder.append_query_name_encoded_string_to (ic.key, a_url) + uri_percent_encoder.append_query_name_encoded_string_to (ic.item.name, a_url) a_url.append_character ('=') - uri_percent_encoder.append_query_value_encoded_string_to (ic.item, a_url) + ic.item.append_query_value_encoded_to (a_url) end end end @@ -315,38 +329,35 @@ feature {NONE} -- Implementation end end - parameters_to_uri_percent_encoded_string (ht: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING_8 - -- Build query urlencoded string using parameters from `ht'. + parameters_to_uri_percent_encoded_string (a_params: HTTP_CLIENT_REQUEST_PARAMETERS [HTTP_CLIENT_REQUEST_PARAMETER]): STRING_8 + -- Build query urlencoded string using parameters from `a_params'. do create Result.make (64) across - ht as ic + a_params as ic loop if not Result.is_empty then Result.append_character ('&') end - uri_percent_encoder.append_query_name_encoded_string_to (ic.key, Result) + uri_percent_encoder.append_query_name_encoded_string_to (ic.item.name, Result) Result.append_character ('=') - uri_percent_encoder.append_query_value_encoded_string_to (ic.item, Result) + ic.item.append_query_value_encoded_to (Result) end end - parameters_to_x_www_form_urlencoded_string (ht: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING_8 - -- Build x-www-form-urlencoded string using parameters from `ht'. + parameters_to_x_www_form_urlencoded_string (a_params: HTTP_CLIENT_REQUEST_PARAMETERS [HTTP_CLIENT_REQUEST_PARAMETER]): STRING_8 + -- Build x-www-form-urlencoded string using parameters from `a_params'. do create Result.make (64) - from - ht.start - until - ht.after + across + a_params as ic loop if not Result.is_empty then Result.append_character ('&') end - Result.append (x_www_form_url_encoder.encoded_string (ht.key_for_iteration)) + x_www_form_url_encoder.append_percent_encoded_string_to (ic.item.name, Result) Result.append_character ('=') - Result.append (x_www_form_url_encoder.encoded_string (ht.item_for_iteration)) - ht.forth + ic.item.append_form_url_encoded_to (Result) end end diff --git a/library/network/http_client/src/implementation/http_client_request_file_parameter.e b/library/network/http_client/src/implementation/http_client_request_file_parameter.e new file mode 100644 index 00000000..15776039 --- /dev/null +++ b/library/network/http_client/src/implementation/http_client_request_file_parameter.e @@ -0,0 +1,155 @@ +note + description: "Summary description for {HTTP_CLIENT_REQUEST_FILE_PARAMETER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_CLIENT_REQUEST_FILE_PARAMETER + +inherit + HTTP_CLIENT_REQUEST_PARAMETER + +create + make_with_path + +feature {NONE} -- Initialization + + make_with_path (a_name: READABLE_STRING_GENERAL; a_path: PATH) + do + set_name (a_name) + location := a_path + if attached a_path.entry as e then + file_name := e.name + end + set_content_type ("application/octet-stream") -- Default + end + +feature -- Access + + count: INTEGER + local + f: RAW_FILE + do + create f.make_with_path (location) + if f.exists and then f.is_access_readable then + Result := f.count + end + end + + location: PATH + + file_name: detachable READABLE_STRING_32 + +feature -- Element change + + set_file_name (fn: detachable READABLE_STRING_GENERAL) + do + if fn = Void then + file_name := Void + else + file_name := fn.to_string_32 + end + end + +feature -- Status report + + exists: BOOLEAN + local + fut: FILE_UTILITIES + do + Result := fut.file_path_exists (location) + end + +feature {NONE} -- Data + + file_content: detachable STRING_8 + require + exists: exists + local + f: RAW_FILE + do + create f.make_with_path (location) + if f.exists and then f.is_access_readable then + create Result.make (f.count) + f.open_read + from + until + f.exhausted or f.end_of_file + loop + f.read_stream_thread_aware (2_048) + Result.append (f.last_string) + end + f.close + end + end + +feature -- Data + + append_file_content_to (a_output: STRING) + -- Append content of file located at `location`to `a_output'. + require + exists: exists + local + f: RAW_FILE + l_buffer_size: INTEGER + do + create f.make_with_path (location) + if f.exists and then f.is_access_readable then + f.open_read + from + l_buffer_size := 2_048 + until + f.exhausted or f.end_of_file + loop + f.read_stream_thread_aware (l_buffer_size) + a_output.append (f.last_string) + end + f.close + end + end + +feature -- Conversion + + append_form_url_encoded_to (a_output: STRING_8) + -- Append as form url encoded string to `a_output`. + do + if exists and then attached file_content as s then + x_www_form_url_encoder.append_percent_encoded_string_to (s, a_output) + else + check exists: False end + end + end + + append_query_value_encoded_to (a_output: STRING_8) + do + if exists and then attached file_content as s then + uri_percent_encoder.append_query_value_encoded_string_to (s, a_output) + else + check exists: False end + end + end + + append_as_mime_encoded_to (a_output: STRING_8) + -- Encoded unicode string for mime value. + -- For instance uploaded filename, or form data key or values. + do + -- FIXME: find the proper encoding! + if exists then + append_file_content_to (a_output) + else + check exists: False end + end + end + +note + copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, 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/network/http_client/src/implementation/http_client_request_form_parameters.e b/library/network/http_client/src/implementation/http_client_request_form_parameters.e new file mode 100644 index 00000000..be047fd0 --- /dev/null +++ b/library/network/http_client/src/implementation/http_client_request_form_parameters.e @@ -0,0 +1,34 @@ +note + description: "Summary description for {HTTP_CLIENT_REQUEST_FORM_PARAMETERS}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_CLIENT_REQUEST_FORM_PARAMETERS + +inherit + HTTP_CLIENT_REQUEST_PARAMETERS [HTTP_CLIENT_REQUEST_PARAMETER] + +create + make + +feature -- Status report + + has_file_parameter: BOOLEAN + -- Has any file parameter? + do + Result := across items as ic some attached {HTTP_CLIENT_REQUEST_FILE_PARAMETER} ic.item end + end + +note + copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, 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/network/http_client/src/implementation/http_client_request_parameter.e b/library/network/http_client/src/implementation/http_client_request_parameter.e new file mode 100644 index 00000000..fcaf13da --- /dev/null +++ b/library/network/http_client/src/implementation/http_client_request_parameter.e @@ -0,0 +1,71 @@ +note + description: "Summary description for {HTTP_CLIENT_REQUEST_PARAMETER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTP_CLIENT_REQUEST_PARAMETER + +feature -- Access + + name: READABLE_STRING_32 + + content_type: detachable READABLE_STRING_8 + + count: INTEGER + -- Integer representing the length of source value. + deferred + end + +feature -- Conversion + + append_form_url_encoded_to (a_output: STRING_8) + -- Append as form url encoded string to `a_output`. + deferred + end + + append_query_value_encoded_to (a_output: STRING_8) + deferred + end + + append_as_mime_encoded_to (a_output: STRING_8) + deferred + end + +feature -- Element change + + set_name (a_name: READABLE_STRING_GENERAL) + do + name := a_name.as_string_32 + end + + set_content_type (ct: detachable READABLE_STRING_8) + do + content_type := ct + end + +feature {NONE} -- Implementation + + x_www_form_url_encoder: X_WWW_FORM_URL_ENCODER + -- Shared x-www-form-urlencoded encoder. + once + create Result + end + + uri_percent_encoder: URI_PERCENT_ENCODER + once + create Result + end + +note + copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, 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/network/http_client/src/implementation/http_client_request_parameters.e b/library/network/http_client/src/implementation/http_client_request_parameters.e new file mode 100644 index 00000000..bb6a7a9b --- /dev/null +++ b/library/network/http_client/src/implementation/http_client_request_parameters.e @@ -0,0 +1,62 @@ +note + description: "Summary description for {HTTP_CLIENT_REQUEST_PARAMETERS}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTP_CLIENT_REQUEST_PARAMETERS [G -> HTTP_CLIENT_REQUEST_PARAMETER] + +inherit + ITERABLE [G] + +feature {NONE} -- Initialization + + make (nb: INTEGER) + do + create items.make (nb) + end + +feature -- Access + + is_empty: BOOLEAN + do + Result := items.is_empty + end + + count: INTEGER + do + Result := items.count + end + +feature -- Element change + + extend, force (i: G) + do + items.force (i) + end + +feature -- Iteration + + new_cursor: ARRAYED_LIST_ITERATION_CURSOR [G] + -- + do + Result := items.new_cursor + end + +feature {NONE} -- Implementation + + items: ARRAYED_LIST [G] + +invariant +note + copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, 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/network/http_client/src/implementation/http_client_request_query_parameters.e b/library/network/http_client/src/implementation/http_client_request_query_parameters.e new file mode 100644 index 00000000..8c541da0 --- /dev/null +++ b/library/network/http_client/src/implementation/http_client_request_query_parameters.e @@ -0,0 +1,26 @@ +note + description: "Summary description for {HTTP_CLIENT_REQUEST_QUERY_PARAMETERS}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_CLIENT_REQUEST_QUERY_PARAMETERS + +inherit + HTTP_CLIENT_REQUEST_PARAMETERS [HTTP_CLIENT_REQUEST_STRING_PARAMETER] + +create + make + +note + copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, 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/network/http_client/src/implementation/http_client_request_string_parameter.e b/library/network/http_client/src/implementation/http_client_request_string_parameter.e new file mode 100644 index 00000000..4a71e853 --- /dev/null +++ b/library/network/http_client/src/implementation/http_client_request_string_parameter.e @@ -0,0 +1,68 @@ +note + description: "Summary description for {HTTP_CLIENT_REQUEST_STRING_PARAMETER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_CLIENT_REQUEST_STRING_PARAMETER + +inherit + HTTP_CLIENT_REQUEST_PARAMETER + +create + make + +feature {NONE} -- Initialization + + make (a_name, a_value: READABLE_STRING_GENERAL) + do + set_name (a_name) + value := a_value.as_string_32 + end + +feature -- Access + + value: READABLE_STRING_32 + + count: INTEGER + do + Result := value.count + end + +feature -- Conversion + + append_form_url_encoded_to (a_output: STRING_8) + -- Append as form url encoded string to `a_output`. + do + x_www_form_url_encoder.append_percent_encoded_string_to (value, a_output) + end + + append_query_value_encoded_to (a_output: STRING_8) + do + uri_percent_encoder.append_query_value_encoded_string_to (value, a_output) + end + + append_as_mime_encoded_to (a_output: STRING_8) + -- Encoded unicode string for mime value. + -- For instance uploaded filename, or form data key or values. + local + utf: UTF_CONVERTER + do + -- FIXME: find the proper encoding! + utf.utf_32_string_into_utf_8_string_8 (value, a_output) + end + +invariant + +note + copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, 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/network/http_client/src/spec/libcurl/libcurl_http_client_request.e b/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e index 1ad66b1d..13e44bbb 100644 --- a/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e +++ b/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e @@ -58,7 +58,6 @@ feature -- Execution ctx: like context p_slist: POINTER retried: BOOLEAN - l_form_data: detachable HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32] l_upload_data: detachable READABLE_STRING_8 l_upload_filename: detachable READABLE_STRING_GENERAL l_headers: like headers @@ -151,70 +150,19 @@ feature -- Execution --| Credentials not provided ... end end - if ctx.has_upload_data then l_upload_data := ctx.upload_data end if ctx.has_upload_filename then l_upload_filename := ctx.upload_filename end - if ctx.has_form_data then - l_form_data := ctx.form_parameters - check non_empty_form_data: not l_form_data.is_empty end - if l_upload_data = Void and l_upload_filename = Void then - -- Send as form-urlencoded - if - attached l_headers.item ("Content-Type") as l_ct - then - if l_ct.starts_with ("application/x-www-form-urlencoded") then - -- Content-Type is already application/x-www-form-urlencoded - l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string - elseif l_ct.starts_with ("multipart/form-data") then - l_use_curl_form := True - else - -- Not supported, use libcurl form. - l_use_curl_form := True - end - else - l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string - end - else - l_use_curl_form := True - end - if l_use_curl_form then - create l_form.make - create l_last.make - from - l_form_data.start - until - l_form_data.after - loop - curl.formadd_string_string (l_form, l_last, - {CURL_FORM_CONSTANTS}.curlform_copyname, l_form_data.key_for_iteration, - {CURL_FORM_CONSTANTS}.curlform_copycontents, l_form_data.item_for_iteration, - {CURL_FORM_CONSTANTS}.curlform_end - ) - l_form_data.forth - end - if l_upload_filename /= Void then - curl.formadd_string_string (l_form, l_last, - {CURL_FORM_CONSTANTS}.curlform_copyname, "file", - {CURL_FORM_CONSTANTS}.curlform_file, l_upload_filename, - {CURL_FORM_CONSTANTS}.curlform_end - ) - l_upload_filename := Void - end - l_last.release_item - curl_easy.setopt_form (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httppost, l_form) - end - end - if l_upload_data /= Void then check post_or_put_request_method: request_method.is_case_insensitive_equal ("POST") or request_method.is_case_insensitive_equal ("PUT") or request_method.is_case_insensitive_equal ("PATCH") end + check no_form_data: not ctx.has_form_data end curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_postfields, l_upload_data) curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_postfieldsize, l_upload_data.count) @@ -224,6 +172,7 @@ feature -- Execution or request_method.is_case_insensitive_equal ("PUT") or request_method.is_case_insensitive_equal ("PATCH") end + check no_form_data: not ctx.has_form_data end create l_upload_file.make_with_name (l_upload_filename) if l_upload_file.exists and then l_upload_file.is_readable then @@ -238,12 +187,59 @@ feature -- Execution l_upload_file.open_read curl_easy.set_curl_function (l_custom_function) end + elseif + ctx.has_form_data and + attached ctx.form_parameters as l_form_data + then + check non_empty_form_data: not l_form_data.is_empty end + -- Send as form-urlencoded + if + attached l_headers.item ("Content-Type") as l_ct + then + if l_ct.starts_with ("application/x-www-form-urlencoded") then + -- Content-Type is already application/x-www-form-urlencoded + l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string + elseif l_ct.starts_with ("multipart/form-data") or l_form_data.has_file_parameter then + l_use_curl_form := True + else + -- Not supported, use libcurl form. + l_use_curl_form := True + end + else + l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string + end + if l_use_curl_form then + create l_form.make + create l_last.make + across + l_form_data as ic + loop + if attached {HTTP_CLIENT_REQUEST_STRING_PARAMETER} ic.item as strparam then + curl.formadd_string_string (l_form, l_last, + {CURL_FORM_CONSTANTS}.curlform_copyname, strparam.name, + {CURL_FORM_CONSTANTS}.curlform_copycontents, strparam.value, + {CURL_FORM_CONSTANTS}.curlform_end + ) + elseif attached {HTTP_CLIENT_REQUEST_FILE_PARAMETER} ic.item as fileparam then + curl.formadd_string_string (l_form, l_last, + {CURL_FORM_CONSTANTS}.curlform_copyname, "file", + {CURL_FORM_CONSTANTS}.curlform_file, fileparam.location.name, + {CURL_FORM_CONSTANTS}.curlform_end + ) + else + check supported_parameter_type: False end + end + end + l_last.release_item + curl_easy.setopt_form (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httppost, l_form) + end else - check no_upload_data: l_upload_data = Void and l_upload_filename = Void end + -- No form, or upload data to send! + check no_data: not (ctx.has_upload_data or ctx.has_upload_filename or ctx.has_form_data) end end end -- ctx /= Void - --| Header + --| Header across l_headers as curs loop diff --git a/library/network/http_client/src/spec/net/net_http_client_request.e b/library/network/http_client/src/spec/net/net_http_client_request.e index 5808b031..3c00f138 100644 --- a/library/network/http_client/src/spec/net/net_http_client_request.e +++ b/library/network/http_client/src/spec/net/net_http_client_request.e @@ -91,8 +91,8 @@ feature -- Access l_authorization: HTTP_AUTHORIZATION l_platform: STRING l_upload_data: detachable READABLE_STRING_8 - l_form_data: detachable HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32] ctx: like context + l_ct: detachable READABLE_STRING_8 l_upload_file: detachable RAW_FILE l_upload_filename: detachable READABLE_STRING_GENERAL l_form_string: STRING @@ -149,7 +149,7 @@ feature -- Access then create l_authorization.make_basic_auth (u_name, u_pass) if attached l_authorization.http_authorization as auth then - headers.extend (auth, "Authorization") + headers.force (auth, "Authorization") end check headers.has_key ("Authorization") end end @@ -176,7 +176,7 @@ feature -- Access else l_platform := "Unknown" end - headers.extend ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent") + headers.force ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent") end -- handle sending data @@ -191,67 +191,52 @@ feature -- Access l_upload_data := ctx.upload_data end - if ctx.has_form_data then - l_form_data := ctx.form_parameters - if l_upload_data = Void and l_upload_filename = Void then - if - attached headers.item ("Content-Type") as l_ct - then - if l_ct.starts_with ("application/x-www-form-urlencoded") then - l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string - elseif l_ct.starts_with ("multipart/form-data") then - -- create form using multipart/form-data encoding - l_boundary := new_mime_boundary (l_form_data) - headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type") - l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary) - else - -- not supported ! - -- Send as form-urlencoded - headers.extend ("application/x-www-form-urlencoded", "Content-Type") - l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string - end - else - -- Send as form-urlencoded - headers.extend ("application/x-www-form-urlencoded", "Content-Type") - l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string - end - headers.extend (l_upload_data.count.out, "Content-Length") - if l_is_chunked_transfer_encoding then - -- Discard chunked transfer encoding - headers.remove ("Transfer-Encoding") - l_is_chunked_transfer_encoding := False - end - elseif l_form_data /= Void then - check l_upload_data = Void end - - -- create form using multipart/form-data encoding - l_boundary := new_mime_boundary (l_form_data) - headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type") - l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary) - headers.extend (l_upload_data.count.out, "Content-Length") - if l_is_chunked_transfer_encoding then - -- Discard chunked transfer encoding - headers.remove ("Transfer-Encoding") - l_is_chunked_transfer_encoding := False - end - end - elseif l_upload_data /= Void then + if l_upload_data /= Void then check ctx.has_upload_data end + check no_form_data: not ctx.has_form_data end if not headers.has ("Content-Type") then - headers.extend ("application/x-www-form-urlencoded", "Content-Type") + headers.force ("application/x-www-form-urlencoded", "Content-Type") end if not l_is_chunked_transfer_encoding then - headers.extend (l_upload_data.count.out, "Content-Length") + headers.force (l_upload_data.count.out, "Content-Length") end elseif l_upload_filename /= Void then check ctx.has_upload_filename end + check no_form_data: not ctx.has_form_data end create l_upload_file.make_with_name (l_upload_filename) if l_upload_file.exists and then l_upload_file.is_access_readable then if not l_is_chunked_transfer_encoding then - headers.extend (l_upload_file.count.out, "Content-Length") + headers.force (l_upload_file.count.out, "Content-Length") end end check l_upload_file /= Void end + elseif + ctx.has_form_data and + attached ctx.form_parameters as l_form_data + then + l_ct := headers.item ("Content-Type") + if l_ct /= Void and then l_ct.starts_with ("application/x-www-form-urlencoded") then + l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string + elseif + (l_ct /= Void and then l_ct.starts_with ("multipart/form-data")) + or l_form_data.has_file_parameter + then + -- create form using multipart/form-data encoding + l_boundary := new_mime_boundary (l_form_data) + headers.force ("multipart/form-data; boundary=" + l_boundary, "Content-Type") + l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_boundary) + else + -- not supported ! + -- Send as form-urlencoded + headers.force ("application/x-www-form-urlencoded", "Content-Type") + l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string + end + headers.force (l_upload_data.count.out, "Content-Length") + if l_is_chunked_transfer_encoding then + -- Discard chunked transfer encoding + headers.remove ("Transfer-Encoding") + l_is_chunked_transfer_encoding := False + end end end @@ -482,14 +467,9 @@ feature {NONE} -- Helpers Result := a_status >= 300 and a_status < 400 end - form_date_and_uploaded_files_to_mime_string (a_form_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]; a_upload_filename: detachable READABLE_STRING_GENERAL; a_mime_boundary: READABLE_STRING_8): STRING + form_date_and_uploaded_files_to_mime_string (a_form_parameters: ITERABLE [HTTP_CLIENT_REQUEST_PARAMETER]; a_mime_boundary: READABLE_STRING_8): STRING -- Form data and uploaded files converted to mime string. -- TODO: design a proper MIME... component. - local - l_path: PATH - l_mime_type: READABLE_STRING_8 - l_upload_file: detachable RAW_FILE - l_mime_type_mapping: HTTP_FILE_EXTENSION_MIME_MAPPING do create Result.make (100) across @@ -500,48 +480,26 @@ feature {NONE} -- Helpers Result.append (http_end_of_header_line) Result.append ("Content-Disposition: form-data; name=") Result.append_character ('%"') - Result.append (string_to_mime_encoded_string (ic.key)) + Result.append (string_to_mime_encoded_string (ic.item.name)) Result.append_character ('%"') - Result.append (http_end_of_header_line) - Result.append (http_end_of_header_line) - Result.append (string_to_mime_encoded_string (ic.item)) - Result.append (http_end_of_header_line) - end - - if a_upload_filename /= Void then - -- get file extension, otherwise set default - create l_mime_type_mapping.make_default - create l_path.make_from_string (a_upload_filename) if - attached l_path.extension as ext and then - attached l_mime_type_mapping.mime_type (ext) as l_mt + attached {HTTP_CLIENT_REQUEST_FILE_PARAMETER} ic.item as fileparam and then + attached fileparam.file_name as fn then - l_mime_type := l_mt - else - l_mime_type := "application/octet-stream" + Result.append ("; filename=") + Result.append_character ('%"') + Result.append (string_to_mime_encoded_string (fn)) + Result.append_character ('%"') end - Result.append ("--") - Result.append (a_mime_boundary) - Result.append (http_end_of_header_line) - Result.append ("Content-Disposition: form-data; name=%"") - Result.append (string_to_mime_encoded_string (a_upload_filename)) - Result.append_character ('%"') - Result.append ("; filename=%"") - Result.append (string_to_mime_encoded_string (a_upload_filename)) - Result.append_character ('%"') - Result.append (http_end_of_header_line) - Result.append ("Content-Type: ") - Result.append (l_mime_type) - Result.append (http_end_of_header_line) - Result.append (http_end_of_header_line) - - create l_upload_file.make_with_path (l_path) - if l_upload_file.exists and then l_upload_file.is_access_readable then - append_file_content_to (l_upload_file, l_upload_file.count, Result) - -- Reset l_upload_file to Void, since the related content is already processed. - l_upload_file := Void + if attached ic.item.content_type as ct then + Result.append (http_end_of_header_line) + Result.append ("Content-Type: ") + Result.append (ct) end Result.append (http_end_of_header_line) + Result.append (http_end_of_header_line) + ic.item.append_as_mime_encoded_to (Result) + Result.append (http_end_of_header_line) end Result.append ("--") Result.append (a_mime_boundary) @@ -893,7 +851,7 @@ feature {NONE} -- Helpers end end - new_mime_boundary (a_data: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING + new_mime_boundary (a_data: ITERABLE [HTTP_CLIENT_REQUEST_PARAMETER]): STRING -- New MIME boundary. local s: STRING @@ -904,7 +862,7 @@ feature {NONE} -- Helpers across a_data as ic loop - i := i + ic.item.count + ic.key.count + i := i + ic.item.count + ic.item.name.count end create ran.set_seed (i) -- FIXME: use a real random seed. ran.start diff --git a/library/network/http_client/tests/test-safe.ecf b/library/network/http_client/tests/test.ecf similarity index 87% rename from library/network/http_client/tests/test-safe.ecf rename to library/network/http_client/tests/test.ecf index 2c0cdb86..49622c2a 100644 --- a/library/network/http_client/tests/test-safe.ecf +++ b/library/network/http_client/tests/test.ecf @@ -1,6 +1,6 @@ - - + + /.git$ @@ -10,7 +10,8 @@ - + + diff --git a/library/network/http_client/tests/test_libcurl_with_web.e b/library/network/http_client/tests/test_libcurl_with_web.e index 69ad2337..842f78d0 100644 --- a/library/network/http_client/tests/test_libcurl_with_web.e +++ b/library/network/http_client/tests/test_libcurl_with_web.e @@ -59,6 +59,11 @@ feature -- Tests test_post_with_file_and_form_data end + libcurl_test_post_with_multiple_file_and_form_data + do + test_post_with_multiple_file_and_form_data + end + libcurl_test_get_with_redirection do test_get_with_redirection diff --git a/library/network/http_client/tests/test_net_with_web.e b/library/network/http_client/tests/test_net_with_web.e index 6a1a112c..14488c99 100644 --- a/library/network/http_client/tests/test_net_with_web.e +++ b/library/network/http_client/tests/test_net_with_web.e @@ -59,6 +59,11 @@ feature -- Tests test_post_with_file_and_form_data end + net_test_post_with_multiple_file_and_form_data + do + test_post_with_multiple_file_and_form_data + end + net_test_get_with_redirection do test_get_with_redirection diff --git a/library/network/http_client/tests/test_with_web_i.e b/library/network/http_client/tests/test_with_web_i.e index 56a1604a..e1539ea4 100644 --- a/library/network/http_client/tests/test_with_web_i.e +++ b/library/network/http_client/tests/test_with_web_i.e @@ -21,8 +21,7 @@ feature -- Initialization on_prepare do Precursor - global_requestbin_path := "/s0jkhhs0" - if global_requestbin_path = Void then + if is_using_requestbin and global_requestbin_path = Void then global_requestbin_path := new_requestbin_path end end @@ -33,7 +32,13 @@ feature -- Factory deferred end -feature -- Requestbin +feature -- Requestbin + + is_using_requestbin: BOOLEAN = False + is_using_mockbincom: BOOLEAN + do + Result := not is_using_requestbin + end global_requestbin_path: detachable READABLE_STRING_8 @@ -42,7 +47,7 @@ feature -- Requestbin i,j: INTEGER do if - attached new_session ("http://requestb.in") as sess and then + attached new_session ("https://requestb.in") as sess and then attached sess.post ("/api/v1/bins", Void, Void) as resp then if resp.error_occurred then @@ -67,13 +72,30 @@ feature -- Requestbin if not Result.starts_with ("/") then Result.prepend_character ('/') end - print ("new_requestbin_path => http://requestb.in" + Result + "?inspect%N") + print ("new_requestbin_path => " + sess.base_url + Result + "?inspect%N") end end end end end + new_web_session: like new_session + do + if is_using_mockbincom then + Result := new_session ("http://mockbin.com/request") + end + if Result = Void and is_using_requestbin then + if attached global_requestbin_path as l_path then + Result := new_session ("https://requestb.in" + l_path) + else + assert ("Has requestbin path", False) + end + end + if Result = Void then + Result := new_session ("http://mockbin.com/request") -- Default + end + end + feature -- Factory test_post_url_encoded @@ -81,288 +103,200 @@ feature -- Factory sess: HTTP_CLIENT_SESSION h: STRING_8 do - if attached global_requestbin_path as requestbin_path then - -- URL ENCODED POST REQUEST - -- check requestbin to ensure the "Hello World" has been received in the raw body - -- also check that User-Agent was sent - create h.make_empty - sess := new_session ("http://requestb.in") - if - attached sess.post (requestbin_path, Void, "Hello World") as res and then - attached res.headers as hds - then - across - hds as c - loop - h.append (c.item.name + ": " + c.item.value + "%R%N") - end - end - print (h) - else - assert ("Has requestbin path", False) + -- URL ENCODED POST REQUEST + -- check requestbin to ensure the "Hello World" has been received in the raw body + -- also check that User-Agent was sent + create h.make_empty + sess := new_web_session + if + attached sess.post ("", Void, "Hello World") as res + then + check_response (res) end end test_post_with_form_data local sess: HTTP_CLIENT_SESSION - h: STRING_8 l_ctx: HTTP_CLIENT_REQUEST_CONTEXT do - if attached global_requestbin_path as requestbin_path then - - -- POST REQUEST WITH FORM DATA - -- check requestbin to ensure the form parameters are correctly received - sess := new_session ("http://requestb.in") - create l_ctx.make - l_ctx.add_form_parameter ("First Key", "First Value") - l_ctx.add_form_parameter ("Second Key", "Second Value") - l_ctx.add_form_parameter ("unicode", {STRING_32} "Hello / 你好 !") - l_ctx.add_form_parameter ({STRING_32} "Field 你好 !", "How are you?") - create h.make_empty - if - attached sess.post (requestbin_path, l_ctx, "") as res and then - attached res.headers as hds - then - across - hds as c - loop - h.append (c.item.name + ": " + c.item.value + "%R%N") - end - end - print (h) - else - assert ("Has requestbin path", False) + -- POST REQUEST WITH FORM DATA + -- check requestbin to ensure the form parameters are correctly received + sess := new_web_session + create l_ctx.make + l_ctx.add_form_parameter ("First Key", "First Value") + l_ctx.add_form_parameter ("Second Key", "Second Value") + l_ctx.add_form_parameter ("unicode", {STRING_32} "Hello / 你好 !") + l_ctx.add_form_parameter ({STRING_32} "Field 你好 !", "How are you?") + if + attached sess.post ("", l_ctx, "") as res + then + check_response (res) end end test_post_with_uncommon_form_data local sess: HTTP_CLIENT_SESSION - h: STRING_8 l_ctx: HTTP_CLIENT_REQUEST_CONTEXT do - if attached global_requestbin_path as requestbin_path then + -- POST REQUEST WITH FORM DATA + -- check requestbin to ensure the form parameters are correctly received + sess := new_web_session + create l_ctx.make - -- POST REQUEST WITH FORM DATA - -- check requestbin to ensure the form parameters are correctly received - sess := new_session ("http://requestb.in") - create l_ctx.make + l_ctx.add_form_parameter ("title", "Eiffel World!") -- space and ! + l_ctx.add_form_parameter ("path", "foo/bar") -- slash + l_ctx.add_form_parameter ("unreserved", ":!@[]{}()*") -- ... + l_ctx.add_form_parameter ("reserved", "+=?&_#_") -- ... + l_ctx.add_form_parameter ("a=b", "a=b") -- equal sign + l_ctx.add_form_parameter ("test", "!$&'()*") -- + l_ctx.add_form_parameter ("lst[a][b]", "[123][456]") -- brackets + l_ctx.add_form_parameter ("pos{1,2}", "loc{a,b}") -- curly brackets + l_ctx.add_form_parameter ("?foo", "?bar") -- question mark + l_ctx.add_form_parameter ("?", "?") -- question mark + l_ctx.add_form_parameter ("&bar", "&bar") -- ampersand + l_ctx.add_form_parameter ("&", "&") -- ampersand - l_ctx.add_form_parameter ("title", "Eiffel World!") -- space and ! - l_ctx.add_form_parameter ("path", "foo/bar") -- slash - l_ctx.add_form_parameter ("unreserved", ":!@[]{}()*") -- ... - l_ctx.add_form_parameter ("reserved", "+=?&_#_") -- ... - l_ctx.add_form_parameter ("a=b", "a=b") -- equal sign - l_ctx.add_form_parameter ("test", "!$&'()*") -- - l_ctx.add_form_parameter ("lst[a][b]", "[123][456]") -- brackets - l_ctx.add_form_parameter ("pos{1,2}", "loc{a,b}") -- curly brackets - l_ctx.add_form_parameter ("?foo", "?bar") -- question mark - l_ctx.add_form_parameter ("?", "?") -- question mark - l_ctx.add_form_parameter ("&bar", "&bar") -- ampersand - l_ctx.add_form_parameter ("&", "&") -- ampersand + assert ("form data well generated", l_ctx.form_parameters_to_x_www_form_url_encoded_string.same_string ("title=Eiffel+World!&path=foo%%2Fbar&unreserved=%%3A!%%40%%5B%%5D%%7B%%7D()*&reserved=%%2B%%3D%%3F%%26_%%23_&a%%3Db=a%%3Db&test=!%%24%%26'()*&lst%%5Ba%%5D%%5Bb%%5D=%%5B123%%5D%%5B456%%5D&pos%%7B1%%2C2%%7D=loc%%7Ba%%2Cb%%7D&%%3Ffoo=%%3Fbar&%%3F=%%3F&%%26bar=%%26bar&%%26=%%26")) - assert ("form data well generated", l_ctx.form_parameters_to_x_www_form_url_encoded_string.same_string ("title=Eiffel+World!&path=foo%%2Fbar&unreserved=%%3A!%%40%%5B%%5D%%7B%%7D()*&reserved=%%2B%%3D%%3F%%26_%%23_&a%%3Db=a%%3Db&test=!%%24%%26'()*&lst%%5Ba%%5D%%5Bb%%5D=%%5B123%%5D%%5B456%%5D&pos%%7B1%%2C2%%7D=loc%%7Ba%%2Cb%%7D&%%3Ffoo=%%3Fbar&%%3F=%%3F&%%26bar=%%26bar&%%26=%%26")) - - create h.make_empty - if - attached sess.post (requestbin_path, l_ctx, "") as res and then - attached res.headers as hds - then - across - hds as c - loop - h.append (c.item.name + ": " + c.item.value + "%R%N") - end - end - print (h) - else - assert ("Has requestbin path", False) + if + attached sess.post ("", l_ctx, "") as res + then + check_response (res) end end test_post_with_file local sess: HTTP_CLIENT_SESSION - h: STRING_8 l_ctx: HTTP_CLIENT_REQUEST_CONTEXT do - if attached global_requestbin_path as requestbin_path then - - -- POST REQUEST WITH A FILE - -- check requestbin to ensure the form parameters are correctly received - -- set filename to a local file - sess := new_session ("http://requestb.in") - create l_ctx.make - l_ctx.set_upload_filename ("test.txt") - create h.make_empty - if - attached sess.post (requestbin_path, l_ctx, Void) as res and then - attached res.headers as hds - then - across - hds as c - loop - h.append (c.item.name + ": " + c.item.value + "%R%N") - end - end - print (h) - else - assert ("Has requestbin path", False) + -- POST REQUEST WITH A FILE + -- check requestbin to ensure the form parameters are correctly received + -- set filename to a local file + sess := new_web_session + create l_ctx.make + l_ctx.set_upload_filename ("test.txt") + if + attached sess.post ("", l_ctx, Void) as res + then + check_response (res) end end test_put_with_file local sess: HTTP_CLIENT_SESSION - h: STRING_8 l_ctx: HTTP_CLIENT_REQUEST_CONTEXT do - if attached global_requestbin_path as requestbin_path then - - -- PUT REQUEST WITH A FILE - -- check requestbin to ensure the file is correctly received - -- set filename to a local file - sess := new_session ("http://requestb.in") - create l_ctx.make - l_ctx.set_upload_filename ("test.txt") - create h.make_empty - if - attached sess.put (requestbin_path, l_ctx, Void) as res and then - attached res.headers as hds - then - across - hds as c - loop - h.append (c.item.name + ": " + c.item.value + "%R%N") - end - end - print (h) - else - assert ("Has requestbin path", False) + -- PUT REQUEST WITH A FILE + -- check requestbin to ensure the file is correctly received + -- set filename to a local file + sess := new_web_session + create l_ctx.make + l_ctx.set_upload_filename ("test.txt") + if + attached sess.put ("", l_ctx, Void) as res + then + check_response (res) end end test_put_with_data local sess: HTTP_CLIENT_SESSION - h: STRING_8 l_ctx: HTTP_CLIENT_REQUEST_CONTEXT do - if attached global_requestbin_path as requestbin_path then - - -- PUT REQUEST WITH A FILE - -- check requestbin to ensure the file is correctly received - -- set filename to a local file - sess := new_session ("http://requestb.in") - create l_ctx.make - l_ctx.set_upload_data ("name=This is a test for http client.%N") - create h.make_empty - if - attached sess.put (requestbin_path, l_ctx, Void) as res and then - attached res.headers as hds - then - across - hds as c - loop - h.append (c.item.name + ": " + c.item.value + "%R%N") - end - end - print (h) - else - assert ("Has requestbin path", False) + -- PUT REQUEST WITH A FILE + -- check requestbin to ensure the file is correctly received + -- set filename to a local file + sess := new_web_session + create l_ctx.make + l_ctx.set_upload_data ("name=This is a test for http client.%N") + if + attached sess.put ("", l_ctx, Void) as res + then + check_response (res) end + end test_post_with_file_and_form_data local sess: HTTP_CLIENT_SESSION - h: STRING_8 l_ctx: HTTP_CLIENT_REQUEST_CONTEXT do - if attached global_requestbin_path as requestbin_path then + -- POST REQUEST WITH A FILE AND FORM DATA + -- check requestbin to ensure the file and form parameters are correctly received + -- set filename to a local file + sess := new_web_session + create l_ctx.make +-- l_ctx.add_file_form_parameter ("image", "test.txt", "image/jpeg") + l_ctx.add_file_form_parameter ("text", "test.txt", "plain/text") + l_ctx.add_form_parameter ("First", "Value") + l_ctx.add_form_parameter ("Second", "and last value") + if + attached sess.post ("", l_ctx, Void) as res + then + check_response (res) + end + end - -- POST REQUEST WITH A FILE AND FORM DATA - -- check requestbin to ensure the file and form parameters are correctly received - -- set filename to a local file - sess := new_session ("http://requestb.in") --- sess := new_session ("http://localhost:9090") - create l_ctx.make --- l_ctx.set_upload_filename ("logo.jpg") - l_ctx.set_upload_filename ("test.txt") - l_ctx.add_form_parameter ("First", "Value") - l_ctx.add_form_parameter ("Second", "and last value") - create h.make_empty - if - attached sess.post (requestbin_path, l_ctx, Void) as res and then - attached res.headers as hds - then - across - hds as c - loop - h.append (c.item.name + ": " + c.item.value + "%R%N") - end - end - print (h) - else - assert ("Has requestbin path", False) + test_post_with_multiple_file_and_form_data + local + sess: HTTP_CLIENT_SESSION + l_ctx: HTTP_CLIENT_REQUEST_CONTEXT + do + -- POST REQUEST WITH A FILE AND FORM DATA + -- check requestbin to ensure the file and form parameters are correctly received + -- set filename to a local file + sess := new_web_session + create l_ctx.make + l_ctx.add_header ("Content-Type", "multipart/form-data") + + l_ctx.add_file_form_parameter ("first_file", "test.txt", "plain/text") + l_ctx.add_file_form_parameter ("image", "logo.jpg", "image/jpeg") + l_ctx.add_form_parameter ("First", "Value") + l_ctx.add_form_parameter ("Second", "and last value") + l_ctx.add_file_form_parameter ("last_file", "test.txt", Void) + + if + attached sess.post ("", l_ctx, Void) as res + then + check_response (res) end end test_post_with_file_using_chunked_transfer_encoding local sess: HTTP_CLIENT_SESSION - h: STRING_8 l_ctx: HTTP_CLIENT_REQUEST_CONTEXT do - if attached global_requestbin_path as requestbin_path then - - -- POST REQUEST WITH A FILE AND FORM DATA - -- check requestbin to ensure the file and form parameters are correctly received - -- set filename to a local file - sess := new_session ("http://requestb.in") - create l_ctx.make - l_ctx.add_header ("Transfer-Encoding", "chunked") - l_ctx.set_upload_filename ("logo.jpg") - create h.make_empty - if - attached sess.post (requestbin_path, l_ctx, Void) as res and then - attached res.headers as hds - then - across - hds as c - loop - h.append (c.item.name + ": " + c.item.value + "%R%N") - end - end - print (h) - else - assert ("Has requestbin path", False) + -- POST REQUEST WITH A FILE AND FORM DATA + -- check requestbin to ensure the file and form parameters are correctly received + -- set filename to a local file + sess := new_web_session + create l_ctx.make + l_ctx.add_header ("Transfer-Encoding", "chunked") + l_ctx.set_upload_filename ("logo.jpg") + if + attached sess.post ("", l_ctx, Void) as res + then + check_response (res) end end test_get_with_redirection local sess: HTTP_CLIENT_SESSION - h: STRING_8 do - if attached global_requestbin_path as requestbin_path then - - -- GET REQUEST, Forwarding (google's first answer is a forward) - -- check headers received (printed in console) - sess := new_session ("http://google.com") - create h.make_empty - if attached sess.get ("/", Void) as res and then attached res.headers as hds then - assert("was redirected", res.redirections_count > 0) - across - hds as c - loop - h.append (c.item.name + ": " + c.item.value + "%R%N") - end - end - print (h) - else - assert ("Has requestbin path", False) + -- GET REQUEST, Forwarding (google's first answer is a forward) + -- check headers received (printed in console) + sess := new_session ("http://google.com") + if attached sess.get ("/", Void) as res then + check_response (res) + assert("was redirected", res.redirections_count > 0) end end @@ -377,6 +311,7 @@ feature -- Factory sess.set_credentials ("test", "test") create ctx.make_with_credentials_required if attached sess.get ("/password-ok.php", ctx) as res then + check_response (res) if attached {READABLE_STRING_8} res.body as l_body then assert ("Fetch all body, including closing html tag", l_body.has_substring ("")) else @@ -388,50 +323,58 @@ feature -- Factory test_get_with_query_parameters local sess: HTTP_CLIENT_SESSION - h: STRING_8 l_ctx: HTTP_CLIENT_REQUEST_CONTEXT q: STRING do - if attached global_requestbin_path as requestbin_path then - - -- GET REQUEST WITH A FILE AND FORM DATA - -- check requestbin to ensure the file and form parameters are correctly received - -- set filename to a local file - sess := new_session ("http://requestb.in") - create l_ctx.make - l_ctx.add_query_parameter ("?", "?first&arg") - l_ctx.add_query_parameter ("title", "Eiffel World!") - l_ctx.add_query_parameter ("path", "foo/bar") - l_ctx.add_query_parameter ("reserved", "+=&?") - l_ctx.add_query_parameter ("unreserved", ":!@'()*") - l_ctx.add_query_parameter ("unsafe", "%"[]{}") - l_ctx.add_query_parameter ("test", "!$&'()*") - l_ctx.add_query_parameter ("a&b", "a&b") - l_ctx.add_query_parameter ("lst[a][b]", "[abc][123]") - l_ctx.add_query_parameter ("foo(a,b)", "bar(1,2)*pi") - create q.make_empty - l_ctx.append_query_parameters_to_url (q) - assert("query", q.same_string ("??=?first%%26arg&title=Eiffel+World!&path=foo/bar&reserved=%%2B=%%26?&unreserved=:!@'()*&unsafe=%%22%%5B%%5D%%7B%%7D&test=!$%%26'()*&a%%26b=a%%26b&lst%%5Ba%%5D%%5Bb%%5D=%%5Babc%%5D%%5B123%%5D&foo(a,b)=bar(1,2)*pi")) + -- GET REQUEST WITH A FILE AND FORM DATA + -- check requestbin to ensure the file and form parameters are correctly received + -- set filename to a local file + sess := new_web_session + create l_ctx.make + l_ctx.add_query_parameter ("?", "?first&arg") + l_ctx.add_query_parameter ("title", "Eiffel World!") + l_ctx.add_query_parameter ("path", "foo/bar") + l_ctx.add_query_parameter ("reserved", "+=&?") + l_ctx.add_query_parameter ("unreserved", ":!@'()*") + l_ctx.add_query_parameter ("unsafe", "%"[]{}") + l_ctx.add_query_parameter ("test", "!$&'()*") + l_ctx.add_query_parameter ("a&b", "a&b") + l_ctx.add_query_parameter ("lst[a][b]", "[abc][123]") + l_ctx.add_query_parameter ("foo(a,b)", "bar(1,2)*pi") + create q.make_empty + l_ctx.append_query_parameters_to_url (q) + assert("query", q.same_string ("??=?first%%26arg&title=Eiffel+World!&path=foo/bar&reserved=%%2B=%%26?&unreserved=:!@'()*&unsafe=%%22%%5B%%5D%%7B%%7D&test=!$%%26'()*&a%%26b=a%%26b&lst%%5Ba%%5D%%5Bb%%5D=%%5Babc%%5D%%5B123%%5D&foo(a,b)=bar(1,2)*pi")) - create h.make_empty - if - attached sess.get (requestbin_path, l_ctx) as res and then - attached res.headers as hds - then - across - hds as c - loop - h.append (c.item.name + ": " + c.item.value + "%R%N") - end - end - print (h) - else - assert ("Has requestbin path", False) + if + attached sess.get ("", l_ctx) as res + then + check_response (res) end end +feature {NONE} -- Implementation + check_response (res: HTTP_CLIENT_RESPONSE) + local + h: STRING + do + assert ("ok", not res.error_occurred) + create h.make_empty + if + attached res.headers as hds + then + across + hds as c + loop + h.append (c.item.name + ": " + c.item.value + "%R%N") + end + end + print (h) + if attached res.body as b then + print (b) + end + end end