Added support for multiple file in form data.

Made clear what is the meaning of upload_filename, upload_data and form_data.
This commit is contained in:
Jocelyn Fiat
2017-06-14 16:19:43 +02:00
parent 0783049fb4
commit 1ec3b8e7a4
13 changed files with 764 additions and 429 deletions

View File

@@ -58,11 +58,11 @@ feature -- Access
-- Specific headers to use in addition to the one set in the related HTTP_CLIENT_SESSION -- 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 --| 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 -- 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 --| 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 -- Form parameters
upload_data: detachable READABLE_STRING_8 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_query_parameter (k: READABLE_STRING_GENERAL; v: READABLE_STRING_GENERAL)
-- Add a query parameter `k=v'. -- Add a query parameter `k=v'.
do 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 end
add_form_parameter (k: READABLE_STRING_GENERAL; v: READABLE_STRING_GENERAL) add_form_parameter (k: READABLE_STRING_GENERAL; v: READABLE_STRING_GENERAL)
-- Add a form parameter `k'= `v'. -- Add a form parameter `k'= `v'.
do 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 end
set_credentials_required (b: BOOLEAN) set_credentials_required (b: BOOLEAN)
@@ -164,7 +176,8 @@ feature -- Element change
-- Set `upload_data' to `a_data' -- Set `upload_data' to `a_data'
--| note: the Current context can have upload_data XOR upload_filename, but not both. --| note: the Current context can have upload_data XOR upload_filename, but not both.
require 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 do
if a_data = Void or else a_data.is_empty then if a_data = Void or else a_data.is_empty then
upload_data := Void upload_data := Void
@@ -180,6 +193,7 @@ feature -- Element change
--| note: the Current context can have upload_data XOR upload_filename, but not both. --| note: the Current context can have upload_data XOR upload_filename, but not both.
require require
has_no_upload_data: (a_fn /= Void and then not a_fn.is_empty) implies not has_upload_data 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 do
if a_fn = Void or else a_fn.is_empty then if a_fn = Void or else a_fn.is_empty then
upload_filename := Void upload_filename := Void
@@ -266,9 +280,9 @@ feature -- URL helpers
a_url.append_character ('&') a_url.append_character ('&')
end end
l_first_param := False 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 ('=') 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 end
end end
@@ -315,38 +329,35 @@ feature {NONE} -- Implementation
end end
end end
parameters_to_uri_percent_encoded_string (ht: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING_8 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 `ht'. -- Build query urlencoded string using parameters from `a_params'.
do do
create Result.make (64) create Result.make (64)
across across
ht as ic a_params as ic
loop loop
if not Result.is_empty then if not Result.is_empty then
Result.append_character ('&') Result.append_character ('&')
end 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 ('=') 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
end end
parameters_to_x_www_form_urlencoded_string (ht: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING_8 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 `ht'. -- Build x-www-form-urlencoded string using parameters from `a_params'.
do do
create Result.make (64) create Result.make (64)
from across
ht.start a_params as ic
until
ht.after
loop loop
if not Result.is_empty then if not Result.is_empty then
Result.append_character ('&') Result.append_character ('&')
end 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_character ('=')
Result.append (x_www_form_url_encoder.encoded_string (ht.item_for_iteration)) ic.item.append_form_url_encoded_to (Result)
ht.forth
end end
end end

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]
-- <Precursor>
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

View File

@@ -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

View File

@@ -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

View File

@@ -58,7 +58,6 @@ feature -- Execution
ctx: like context ctx: like context
p_slist: POINTER p_slist: POINTER
retried: BOOLEAN retried: BOOLEAN
l_form_data: detachable HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]
l_upload_data: detachable READABLE_STRING_8 l_upload_data: detachable READABLE_STRING_8
l_upload_filename: detachable READABLE_STRING_GENERAL l_upload_filename: detachable READABLE_STRING_GENERAL
l_headers: like headers l_headers: like headers
@@ -151,70 +150,19 @@ feature -- Execution
--| Credentials not provided ... --| Credentials not provided ...
end end
end end
if ctx.has_upload_data then if ctx.has_upload_data then
l_upload_data := ctx.upload_data l_upload_data := ctx.upload_data
end end
if ctx.has_upload_filename then if ctx.has_upload_filename then
l_upload_filename := ctx.upload_filename l_upload_filename := ctx.upload_filename
end 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 if l_upload_data /= Void then
check check
post_or_put_request_method: request_method.is_case_insensitive_equal ("POST") 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 ("PUT")
or request_method.is_case_insensitive_equal ("PATCH") or request_method.is_case_insensitive_equal ("PATCH")
end 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_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) 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 ("PUT")
or request_method.is_case_insensitive_equal ("PATCH") or request_method.is_case_insensitive_equal ("PATCH")
end end
check no_form_data: not ctx.has_form_data end
create l_upload_file.make_with_name (l_upload_filename) create l_upload_file.make_with_name (l_upload_filename)
if l_upload_file.exists and then l_upload_file.is_readable then if l_upload_file.exists and then l_upload_file.is_readable then
@@ -238,12 +187,59 @@ feature -- Execution
l_upload_file.open_read l_upload_file.open_read
curl_easy.set_curl_function (l_custom_function) curl_easy.set_curl_function (l_custom_function)
end 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 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
end -- ctx /= Void end -- ctx /= Void
--| Header --| Header
across across
l_headers as curs l_headers as curs
loop loop

View File

@@ -91,8 +91,8 @@ feature -- Access
l_authorization: HTTP_AUTHORIZATION l_authorization: HTTP_AUTHORIZATION
l_platform: STRING l_platform: STRING
l_upload_data: detachable READABLE_STRING_8 l_upload_data: detachable READABLE_STRING_8
l_form_data: detachable HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]
ctx: like context ctx: like context
l_ct: detachable READABLE_STRING_8
l_upload_file: detachable RAW_FILE l_upload_file: detachable RAW_FILE
l_upload_filename: detachable READABLE_STRING_GENERAL l_upload_filename: detachable READABLE_STRING_GENERAL
l_form_string: STRING l_form_string: STRING
@@ -149,7 +149,7 @@ feature -- Access
then then
create l_authorization.make_basic_auth (u_name, u_pass) create l_authorization.make_basic_auth (u_name, u_pass)
if attached l_authorization.http_authorization as auth then if attached l_authorization.http_authorization as auth then
headers.extend (auth, "Authorization") headers.force (auth, "Authorization")
end end
check headers.has_key ("Authorization") end check headers.has_key ("Authorization") end
end end
@@ -176,7 +176,7 @@ feature -- Access
else else
l_platform := "Unknown" l_platform := "Unknown"
end end
headers.extend ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent") headers.force ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent")
end end
-- handle sending data -- handle sending data
@@ -191,67 +191,52 @@ feature -- Access
l_upload_data := ctx.upload_data l_upload_data := ctx.upload_data
end end
if ctx.has_form_data then if l_upload_data /= Void 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
check ctx.has_upload_data end check ctx.has_upload_data end
check no_form_data: not ctx.has_form_data end
if not headers.has ("Content-Type") then 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 end
if not l_is_chunked_transfer_encoding then 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 end
elseif l_upload_filename /= Void then elseif l_upload_filename /= Void then
check ctx.has_upload_filename end 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) 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 l_upload_file.exists and then l_upload_file.is_access_readable then
if not l_is_chunked_transfer_encoding 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
end end
check l_upload_file /= Void 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
end end
@@ -482,14 +467,9 @@ feature {NONE} -- Helpers
Result := a_status >= 300 and a_status < 400 Result := a_status >= 300 and a_status < 400
end 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. -- Form data and uploaded files converted to mime string.
-- TODO: design a proper MIME... component. -- 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 do
create Result.make (100) create Result.make (100)
across across
@@ -500,48 +480,26 @@ feature {NONE} -- Helpers
Result.append (http_end_of_header_line) Result.append (http_end_of_header_line)
Result.append ("Content-Disposition: form-data; name=") Result.append ("Content-Disposition: form-data; name=")
Result.append_character ('%"') 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_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 if
attached l_path.extension as ext and then attached {HTTP_CLIENT_REQUEST_FILE_PARAMETER} ic.item as fileparam and then
attached l_mime_type_mapping.mime_type (ext) as l_mt attached fileparam.file_name as fn
then then
l_mime_type := l_mt Result.append ("; filename=")
else Result.append_character ('%"')
l_mime_type := "application/octet-stream" Result.append (string_to_mime_encoded_string (fn))
Result.append_character ('%"')
end end
Result.append ("--") if attached ic.item.content_type as ct then
Result.append (a_mime_boundary) Result.append (http_end_of_header_line)
Result.append (http_end_of_header_line) Result.append ("Content-Type: ")
Result.append ("Content-Disposition: form-data; name=%"") Result.append (ct)
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
end end
Result.append (http_end_of_header_line) 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 end
Result.append ("--") Result.append ("--")
Result.append (a_mime_boundary) Result.append (a_mime_boundary)
@@ -893,7 +851,7 @@ feature {NONE} -- Helpers
end end
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. -- New MIME boundary.
local local
s: STRING s: STRING
@@ -904,7 +862,7 @@ feature {NONE} -- Helpers
across across
a_data as ic a_data as ic
loop loop
i := i + ic.item.count + ic.key.count i := i + ic.item.count + ic.item.name.count
end end
create ran.set_seed (i) -- FIXME: use a real random seed. create ran.set_seed (i) -- FIXME: use a real random seed.
ran.start ran.start

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="test_http_client" uuid="920E5C50-41E1-4DAC-8D48-D9C860E49228"> <system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="testing_http_client" uuid="920E5C50-41E1-4DAC-8D48-D9C860E49228">
<target name="test_http_client"> <target name="testing_http_client">
<root class="TEST" feature="make"/> <root class="TEST" feature="make"/>
<file_rule> <file_rule>
<exclude>/.git$</exclude> <exclude>/.git$</exclude>
@@ -10,7 +10,8 @@
<option warning="true" void_safety="all"> <option warning="true" void_safety="all">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/> <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option> </option>
<variable name="netssl_http_client_enabled" value="false"/> <variable name="ssl_enabled" value="true"/>
<variable name="netssl_http_client_enabled" value="true"/>
<variable name="net_http_client_disabled" value="false"/> <variable name="net_http_client_disabled" value="false"/>
<variable name="libcurl_http_client_disabled" value="false"/> <variable name="libcurl_http_client_disabled" value="false"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>

View File

@@ -59,6 +59,11 @@ feature -- Tests
test_post_with_file_and_form_data test_post_with_file_and_form_data
end 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 libcurl_test_get_with_redirection
do do
test_get_with_redirection test_get_with_redirection

View File

@@ -59,6 +59,11 @@ feature -- Tests
test_post_with_file_and_form_data test_post_with_file_and_form_data
end 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 net_test_get_with_redirection
do do
test_get_with_redirection test_get_with_redirection

View File

@@ -21,8 +21,7 @@ feature -- Initialization
on_prepare on_prepare
do do
Precursor Precursor
global_requestbin_path := "/s0jkhhs0" if is_using_requestbin and global_requestbin_path = Void then
if global_requestbin_path = Void then
global_requestbin_path := new_requestbin_path global_requestbin_path := new_requestbin_path
end end
end end
@@ -35,6 +34,12 @@ feature -- Factory
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 global_requestbin_path: detachable READABLE_STRING_8
new_requestbin_path: detachable STRING new_requestbin_path: detachable STRING
@@ -42,7 +47,7 @@ feature -- Requestbin
i,j: INTEGER i,j: INTEGER
do do
if 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 attached sess.post ("/api/v1/bins", Void, Void) as resp
then then
if resp.error_occurred then if resp.error_occurred then
@@ -67,13 +72,30 @@ feature -- Requestbin
if not Result.starts_with ("/") then if not Result.starts_with ("/") then
Result.prepend_character ('/') Result.prepend_character ('/')
end 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 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 feature -- Factory
test_post_url_encoded test_post_url_encoded
@@ -81,288 +103,200 @@ feature -- Factory
sess: HTTP_CLIENT_SESSION sess: HTTP_CLIENT_SESSION
h: STRING_8 h: STRING_8
do do
if attached global_requestbin_path as requestbin_path then -- URL ENCODED POST REQUEST
-- URL ENCODED POST REQUEST -- check requestbin to ensure the "Hello World" has been received in the raw body
-- check requestbin to ensure the "Hello World" has been received in the raw body -- also check that User-Agent was sent
-- also check that User-Agent was sent create h.make_empty
create h.make_empty sess := new_web_session
sess := new_session ("http://requestb.in") if
if attached sess.post ("", Void, "Hello World") as res
attached sess.post (requestbin_path, Void, "Hello World") as res and then then
attached res.headers as hds check_response (res)
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)
end end
end end
test_post_with_form_data test_post_with_form_data
local local
sess: HTTP_CLIENT_SESSION sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
do 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
-- POST REQUEST WITH FORM DATA sess := new_web_session
-- check requestbin to ensure the form parameters are correctly received create l_ctx.make
sess := new_session ("http://requestb.in") l_ctx.add_form_parameter ("First Key", "First Value")
create l_ctx.make l_ctx.add_form_parameter ("Second Key", "Second Value")
l_ctx.add_form_parameter ("First Key", "First Value") l_ctx.add_form_parameter ("unicode", {STRING_32} "Hello / 你好 !")
l_ctx.add_form_parameter ("Second Key", "Second Value") l_ctx.add_form_parameter ({STRING_32} "Field 你好 !", "How are you?")
l_ctx.add_form_parameter ("unicode", {STRING_32} "Hello / 你好 !") if
l_ctx.add_form_parameter ({STRING_32} "Field 你好 !", "How are you?") attached sess.post ("", l_ctx, "") as res
create h.make_empty then
if check_response (res)
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)
end end
end end
test_post_with_uncommon_form_data test_post_with_uncommon_form_data
local local
sess: HTTP_CLIENT_SESSION sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
do 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 l_ctx.add_form_parameter ("title", "Eiffel World!") -- space and !
-- check requestbin to ensure the form parameters are correctly received l_ctx.add_form_parameter ("path", "foo/bar") -- slash
sess := new_session ("http://requestb.in") l_ctx.add_form_parameter ("unreserved", ":!@[]{}()*") -- ...
create l_ctx.make 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 ! 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"))
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")) if
attached sess.post ("", l_ctx, "") as res
create h.make_empty then
if check_response (res)
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)
end end
end end
test_post_with_file test_post_with_file
local local
sess: HTTP_CLIENT_SESSION sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
do 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
-- POST REQUEST WITH A FILE -- set filename to a local file
-- check requestbin to ensure the form parameters are correctly received sess := new_web_session
-- set filename to a local file create l_ctx.make
sess := new_session ("http://requestb.in") l_ctx.set_upload_filename ("test.txt")
create l_ctx.make if
l_ctx.set_upload_filename ("test.txt") attached sess.post ("", l_ctx, Void) as res
create h.make_empty then
if check_response (res)
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)
end end
end end
test_put_with_file test_put_with_file
local local
sess: HTTP_CLIENT_SESSION sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
do do
if attached global_requestbin_path as requestbin_path then -- PUT REQUEST WITH A FILE
-- check requestbin to ensure the file is correctly received
-- PUT REQUEST WITH A FILE -- set filename to a local file
-- check requestbin to ensure the file is correctly received sess := new_web_session
-- set filename to a local file create l_ctx.make
sess := new_session ("http://requestb.in") l_ctx.set_upload_filename ("test.txt")
create l_ctx.make if
l_ctx.set_upload_filename ("test.txt") attached sess.put ("", l_ctx, Void) as res
create h.make_empty then
if check_response (res)
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)
end end
end end
test_put_with_data test_put_with_data
local local
sess: HTTP_CLIENT_SESSION sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
do do
if attached global_requestbin_path as requestbin_path then -- PUT REQUEST WITH A FILE
-- check requestbin to ensure the file is correctly received
-- PUT REQUEST WITH A FILE -- set filename to a local file
-- check requestbin to ensure the file is correctly received sess := new_web_session
-- set filename to a local file create l_ctx.make
sess := new_session ("http://requestb.in") l_ctx.set_upload_data ("name=This is a test for http client.%N")
create l_ctx.make if
l_ctx.set_upload_data ("name=This is a test for http client.%N") attached sess.put ("", l_ctx, Void) as res
create h.make_empty then
if check_response (res)
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)
end end
end end
test_post_with_file_and_form_data test_post_with_file_and_form_data
local local
sess: HTTP_CLIENT_SESSION sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
do 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 test_post_with_multiple_file_and_form_data
-- check requestbin to ensure the file and form parameters are correctly received local
-- set filename to a local file sess: HTTP_CLIENT_SESSION
sess := new_session ("http://requestb.in") l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
-- sess := new_session ("http://localhost:9090") do
create l_ctx.make -- POST REQUEST WITH A FILE AND FORM DATA
-- l_ctx.set_upload_filename ("logo.jpg") -- check requestbin to ensure the file and form parameters are correctly received
l_ctx.set_upload_filename ("test.txt") -- set filename to a local file
l_ctx.add_form_parameter ("First", "Value") sess := new_web_session
l_ctx.add_form_parameter ("Second", "and last value") create l_ctx.make
create h.make_empty l_ctx.add_header ("Content-Type", "multipart/form-data")
if
attached sess.post (requestbin_path, l_ctx, Void) as res and then l_ctx.add_file_form_parameter ("first_file", "test.txt", "plain/text")
attached res.headers as hds l_ctx.add_file_form_parameter ("image", "logo.jpg", "image/jpeg")
then l_ctx.add_form_parameter ("First", "Value")
across l_ctx.add_form_parameter ("Second", "and last value")
hds as c l_ctx.add_file_form_parameter ("last_file", "test.txt", Void)
loop
h.append (c.item.name + ": " + c.item.value + "%R%N") if
end attached sess.post ("", l_ctx, Void) as res
end then
print (h) check_response (res)
else
assert ("Has requestbin path", False)
end end
end end
test_post_with_file_using_chunked_transfer_encoding test_post_with_file_using_chunked_transfer_encoding
local local
sess: HTTP_CLIENT_SESSION sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
do 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
-- POST REQUEST WITH A FILE AND FORM DATA -- set filename to a local file
-- check requestbin to ensure the file and form parameters are correctly received sess := new_web_session
-- set filename to a local file create l_ctx.make
sess := new_session ("http://requestb.in") l_ctx.add_header ("Transfer-Encoding", "chunked")
create l_ctx.make l_ctx.set_upload_filename ("logo.jpg")
l_ctx.add_header ("Transfer-Encoding", "chunked") if
l_ctx.set_upload_filename ("logo.jpg") attached sess.post ("", l_ctx, Void) as res
create h.make_empty then
if check_response (res)
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)
end end
end end
test_get_with_redirection test_get_with_redirection
local local
sess: HTTP_CLIENT_SESSION sess: HTTP_CLIENT_SESSION
h: STRING_8
do 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)
-- GET REQUEST, Forwarding (google's first answer is a forward) sess := new_session ("http://google.com")
-- check headers received (printed in console) if attached sess.get ("/", Void) as res then
sess := new_session ("http://google.com") check_response (res)
create h.make_empty assert("was redirected", res.redirections_count > 0)
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)
end end
end end
@@ -377,6 +311,7 @@ feature -- Factory
sess.set_credentials ("test", "test") sess.set_credentials ("test", "test")
create ctx.make_with_credentials_required create ctx.make_with_credentials_required
if attached sess.get ("/password-ok.php", ctx) as res then 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 if attached {READABLE_STRING_8} res.body as l_body then
assert ("Fetch all body, including closing html tag", l_body.has_substring ("</html>")) assert ("Fetch all body, including closing html tag", l_body.has_substring ("</html>"))
else else
@@ -388,50 +323,58 @@ feature -- Factory
test_get_with_query_parameters test_get_with_query_parameters
local local
sess: HTTP_CLIENT_SESSION sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
q: STRING q: STRING
do 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
-- GET REQUEST WITH A FILE AND FORM DATA -- set filename to a local file
-- check requestbin to ensure the file and form parameters are correctly received sess := new_web_session
-- set filename to a local file create l_ctx.make
sess := new_session ("http://requestb.in") l_ctx.add_query_parameter ("?", "?first&arg")
create l_ctx.make l_ctx.add_query_parameter ("title", "Eiffel World!")
l_ctx.add_query_parameter ("?", "?first&arg") l_ctx.add_query_parameter ("path", "foo/bar")
l_ctx.add_query_parameter ("title", "Eiffel World!") l_ctx.add_query_parameter ("reserved", "+=&?")
l_ctx.add_query_parameter ("path", "foo/bar") l_ctx.add_query_parameter ("unreserved", ":!@'()*")
l_ctx.add_query_parameter ("reserved", "+=&?") l_ctx.add_query_parameter ("unsafe", "%"[]{}")
l_ctx.add_query_parameter ("unreserved", ":!@'()*") l_ctx.add_query_parameter ("test", "!$&'()*")
l_ctx.add_query_parameter ("unsafe", "%"[]{}") l_ctx.add_query_parameter ("a&b", "a&b")
l_ctx.add_query_parameter ("test", "!$&'()*") l_ctx.add_query_parameter ("lst[a][b]", "[abc][123]")
l_ctx.add_query_parameter ("a&b", "a&b") l_ctx.add_query_parameter ("foo(a,b)", "bar(1,2)*pi")
l_ctx.add_query_parameter ("lst[a][b]", "[abc][123]") create q.make_empty
l_ctx.add_query_parameter ("foo(a,b)", "bar(1,2)*pi") l_ctx.append_query_parameters_to_url (q)
create q.make_empty 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"))
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
if attached sess.get ("", l_ctx) as res
attached sess.get (requestbin_path, l_ctx) as res and then then
attached res.headers as hds check_response (res)
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)
end end
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 end