diff --git a/contrib/ise_library/cURL b/contrib/ise_library/cURL
index ad2a498f..c4e25a30 160000
--- a/contrib/ise_library/cURL
+++ b/contrib/ise_library/cURL
@@ -1 +1 @@
-Subproject commit ad2a498fc067926713ffdc0a9db99358f142fada
+Subproject commit c4e25a309132a7adced96f5ac1e278eb44582945
diff --git a/library/client/http_client/http_client-safe.ecf b/library/client/http_client/http_client-safe.ecf
index de757e10..34db8bfd 100644
--- a/library/client/http_client/http_client-safe.ecf
+++ b/library/client/http_client/http_client-safe.ecf
@@ -13,12 +13,12 @@
-
+
-
+
diff --git a/library/client/http_client/http_client.ecf b/library/client/http_client/http_client.ecf
index a4ec1415..ffce8e4e 100644
--- a/library/client/http_client/http_client.ecf
+++ b/library/client/http_client/http_client.ecf
@@ -13,12 +13,12 @@
-
+
-
+
diff --git a/library/client/http_client/src/http_client_request.e b/library/client/http_client/src/http_client_request.e
index fbdc8b92..11a485ab 100644
--- a/library/client/http_client/src/http_client_request.e
+++ b/library/client/http_client/src/http_client_request.e
@@ -31,6 +31,14 @@ feature {NONE} -- Initialization
context: detachable HTTP_CLIENT_REQUEST_CONTEXT
+feature -- Status report
+
+ is_debug: BOOLEAN
+ -- Debug mode enabled?
+ do
+ Result := session.is_debug
+ end
+
feature -- Access
request_method: READABLE_STRING_8
diff --git a/library/client/http_client/src/http_client_session.e b/library/client/http_client/src/http_client_session.e
index af9cb3e7..e1b8bdf2 100644
--- a/library/client/http_client/src/http_client_session.e
+++ b/library/client/http_client/src/http_client_session.e
@@ -122,12 +122,16 @@ feature -- Basic operation
deferred
end
+feature -- Status report
+
+ is_debug: BOOLEAN
+ -- Produce debug output
+
feature -- Settings
timeout: INTEGER
-- HTTP transaction timeout in seconds. Defaults to 5 seconds.
-
connect_timeout: INTEGER
-- HTTP connection timeout in seconds. Defaults to 1 second.
@@ -173,7 +177,15 @@ feature -- Authentication
credentials: detachable READABLE_STRING_32
-feature -- Change
+
+feature -- Status setting
+
+ set_is_debug (b: BOOLEAN)
+ do
+ is_debug := b
+ end
+
+feature -- Element change
set_base_url (u: like base_url)
do
diff --git a/library/client/http_client/src/spec/libcurl/libcurl_http_client_request.e b/library/client/http_client/src/spec/libcurl/libcurl_http_client_request.e
index e346b3b3..ce81683e 100644
--- a/library/client/http_client/src/spec/libcurl/libcurl_http_client_request.e
+++ b/library/client/http_client/src/spec/libcurl/libcurl_http_client_request.e
@@ -48,38 +48,173 @@ feature -- Execution
l_result: INTEGER
l_curl_string: CURL_STRING
l_url: READABLE_STRING_8
- p: POINTER
- l_form, l_last: CURL_FORM
- curl: CURL_EXTERNALS
- curl_easy: CURL_EASY_EXTERNALS
+ l_form: detachable CURL_FORM
+ l_last: CURL_FORM
+ l_upload_file: detachable RAW_FILE
+ l_uploade_file_read_function: detachable LIBCURL_UPLOAD_FILE_READ_FUNCTION
+ curl: detachable CURL_EXTERNALS
+ curl_easy: detachable CURL_EASY_EXTERNALS
curl_handle: POINTER
ctx: like context
- l_proxy: like proxy
+ p_slist: POINTER
+ retried: BOOLEAN
do
- ctx := context
- curl := session.curl
- curl_easy := session.curl_easy
+ if not retried then
+ curl := session.curl
+ curl_easy := session.curl_easy
+ curl_handle := curl_easy.init
+ curl.global_init
- l_url := url
- if ctx /= Void then
- if attached ctx.query_parameters as l_query_params then
- from
- l_query_params.start
- until
- l_query_params.after
- loop
- append_parameters_to_url (l_url, <<[l_query_params.key_for_iteration, urlencode (l_query_params.item_for_iteration)]>>)
- l_query_params.forth
+ ctx := context
+
+ --| Configure cURL session
+ initialize_curl_session (ctx, curl, curl_easy, curl_handle)
+
+ --| URL
+ l_url := url
+ if ctx /= Void then
+ if attached ctx.query_parameters as l_query_params then
+ from
+ l_query_params.start
+ until
+ l_query_params.after
+ loop
+ append_parameters_to_url (l_url, <<[l_query_params.key_for_iteration, urlencode (l_query_params.item_for_iteration)]>>)
+ l_query_params.forth
+ end
end
end
+
+ debug ("service")
+ io.put_string ("SERVICE: " + l_url)
+ io.put_new_line
+ end
+ curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_url, l_url)
+
+ --| Header
+ if attached headers as l_headers then
+ across
+ l_headers as curs
+ loop
+ p_slist := curl.slist_append (p_slist, curs.key + ": " + curs.item)
+ end
+ end
+ p_slist := curl.slist_append (p_slist, "Expect:")
+ curl_easy.setopt_slist (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpheader, p_slist)
+
+ --| Credential
+ if ctx /= Void and then ctx.credentials_required then
+ if attached credentials as l_credentials then
+ inspect auth_type_id
+ when {HTTP_CLIENT_CONSTANTS}.Auth_type_none then
+ curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_none)
+ when {HTTP_CLIENT_CONSTANTS}.Auth_type_basic then
+ curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_basic)
+ when {HTTP_CLIENT_CONSTANTS}.Auth_type_digest then
+ curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_digest)
+ when {HTTP_CLIENT_CONSTANTS}.Auth_type_any then
+ curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_any)
+ when {HTTP_CLIENT_CONSTANTS}.Auth_type_anysafe then
+ curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_anysafe)
+ else
+ end
+
+ curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_userpwd, l_credentials)
+ else
+ --| Credentials not prov ided ...
+ end
+ end
+
+ if ctx /= Void and then ctx.has_form_data then
+ if attached ctx.form_parameters as l_forms and then not l_forms.is_empty then
+ create l_form.make
+ create l_last.make
+ from
+ l_forms.start
+ until
+ l_forms.after
+ loop
+ curl.formadd_string_string (l_form, l_last, {CURL_FORM_CONSTANTS}.curlform_copyname, l_forms.key_for_iteration, {CURL_FORM_CONSTANTS}.CURLFORM_COPYCONTENTS, l_forms.item_for_iteration, {CURL_FORM_CONSTANTS}.CURLFORM_END)
+ l_forms.forth
+ end
+ l_last.release_item
+ curl_easy.setopt_form (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httppost, l_form)
+ end
+ end
+ if ctx /= Void then
+ if request_method.is_case_insensitive_equal ("POST") or request_method.is_case_insensitive_equal ("PUT") then
+ if ctx.has_upload_data and then attached ctx.upload_data as l_upload_data then
+ 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)
+ end
+ if ctx.has_upload_filename and then attached ctx.upload_filename as l_upload_filename then
+ create l_upload_file.make (l_upload_filename)
+ if l_upload_file.exists and then l_upload_file.is_readable then
+ curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_upload, 1)
+
+ curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_infilesize, l_upload_file.count)
+ -- specify callback read function for upload file
+ create l_uploade_file_read_function.make_with_file (l_upload_file)
+ l_upload_file.open_read
+ curl_easy.set_curl_function (l_uploade_file_read_function)
+ curl_easy.set_read_function (curl_handle)
+ end
+ end
+ end
+ end
+
+ curl_easy.set_read_function (curl_handle)
+ curl_easy.set_write_function (curl_handle)
+ if is_debug then
+ curl_easy.set_debug_function (curl_handle)
+ curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_verbose, 1)
+ end
+ create l_curl_string.make_empty
+ curl_easy.setopt_curl_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_writedata, l_curl_string)
+
+ create Result.make
+ l_result := curl_easy.perform (curl_handle)
+
+ if l_result = {CURL_CODES}.curle_ok then
+ Result.status := response_status_code (curl_easy, curl_handle)
+ set_header_and_body_to (l_curl_string.string, Result)
+ else
+ Result.set_error_occurred (True)
+ Result.status := response_status_code (curl_easy, curl_handle)
+ end
+
+ curl.global_cleanup
+ curl_easy.cleanup (curl_handle)
+ else
+ create Result.make
+ Result.set_error_occurred (True)
end
+ if l_form /= Void then
+ l_form.dispose
+ end
+ if curl /= Void and then p_slist /= default_pointer then
+ curl.slist_free_all (p_slist)
+ end
+ if l_upload_file /= Void and then not l_upload_file.is_closed then
+ l_upload_file.close
+ end
+ rescue
+ retried := True
+ if curl /= Void then
+ curl.global_cleanup
+ curl := Void
+ end
+ if curl_easy /= Void and curl_handle /= default_pointer then
+ curl_easy.cleanup (curl_handle)
+ curl_easy := Void
+ end
+ retry
+ end
- --| Configure cURL session
- curl_handle := curl_easy.init
-
- --| URL
- curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_url, l_url)
-
+ initialize_curl_session (ctx: like context; curl: CURL_EXTERNALS; curl_easy: CURL_EASY_EXTERNALS; curl_handle: POINTER)
+ local
+ l_proxy: like proxy
+ do
--| RESPONSE HEADERS
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_header, 1)
@@ -118,6 +253,7 @@ feature -- Execution
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_ssl_verifypeer, 0)
end
+ --| Request method
if request_method.is_case_insensitive_equal ("GET") then
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpget, 1)
elseif request_method.is_case_insensitive_equal ("POST") then
@@ -132,98 +268,6 @@ feature -- Execution
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_customrequest, request_method)
--| ignored
end
-
- --| Credential
- if ctx /= Void and then ctx.credentials_required then
- if attached credentials as l_credentials then
- inspect auth_type_id
- when {HTTP_CLIENT_CONSTANTS}.Auth_type_none then
- curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_none)
- when {HTTP_CLIENT_CONSTANTS}.Auth_type_basic then
- curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_basic)
- when {HTTP_CLIENT_CONSTANTS}.Auth_type_digest then
- curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_digest)
- when {HTTP_CLIENT_CONSTANTS}.Auth_type_any then
- curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_any)
- when {HTTP_CLIENT_CONSTANTS}.Auth_type_anysafe then
- curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpauth, {CURL_OPT_CONSTANTS}.curlauth_anysafe)
- else
- end
-
- curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_userpwd, l_credentials)
- else
- --| Credentials not prov ided ...
- end
- end
-
- if ctx /= Void and then ctx.has_form_data then
- if attached ctx.form_parameters as l_forms and then not l_forms.is_empty then
--- curl_easy.set_debug_function (curl_handle)
--- curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_verbose, 1)
-
- create l_form.make
- create l_last.make
- from
- l_forms.start
- until
- l_forms.after
- loop
- curl.formadd_string_string (l_form, l_last, {CURL_FORM_CONSTANTS}.CURLFORM_COPYNAME, l_forms.key_for_iteration, {CURL_FORM_CONSTANTS}.CURLFORM_COPYCONTENTS, l_forms.item_for_iteration, {CURL_FORM_CONSTANTS}.CURLFORM_END)
- l_forms.forth
- end
- l_last.release_item
- curl_easy.setopt_form (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httppost, l_form)
- end
- end
- if ctx /= Void then
- if request_method.is_case_insensitive_equal ("POST") or request_method.is_case_insensitive_equal ("PUT") then
- if ctx.has_upload_data and then attached ctx.upload_data as l_upload_data then
- 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)
- end
- if ctx.has_upload_filename and then attached ctx.upload_filename as l_upload_filename then
--- 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)
---| Not Yet Implemented
- end
- end
- end
-
- curl.global_init
- if attached headers as l_headers then
- across
- l_headers as curs
- loop
- p := curl.slist_append (p, curs.key + ": " + curs.item)
- end
- end
- p := curl.slist_append (p, "Expect:")
- curl_easy.setopt_slist (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpheader, p)
-
- curl.global_cleanup
-
- curl_easy.set_read_function (curl_handle)
- curl_easy.set_write_function (curl_handle)
- create l_curl_string.make_empty
- curl_easy.setopt_curl_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_writedata, l_curl_string)
-
- debug ("service")
- io.put_string ("SERVICE: " + l_url)
- io.put_new_line
- end
-
- create Result.make
- l_result := curl_easy.perform (curl_handle)
-
- if l_result = {CURL_CODES}.curle_ok then
- Result.status := response_status_code (curl_easy, curl_handle)
- set_header_and_body_to (l_curl_string.string, Result)
- else
- Result.set_error_occurred (True)
- Result.status := response_status_code (curl_easy, curl_handle)
- end
-
- curl_easy.cleanup (curl_handle)
end
feature {NONE} -- Implementation
diff --git a/library/client/http_client/src/spec/libcurl/libcurl_upload_file_read_function.e b/library/client/http_client/src/spec/libcurl/libcurl_upload_file_read_function.e
new file mode 100644
index 00000000..317be974
--- /dev/null
+++ b/library/client/http_client/src/spec/libcurl/libcurl_upload_file_read_function.e
@@ -0,0 +1,78 @@
+note
+ description: "[
+ LIBCURL_UPLOAD_FILE_READ_FUNCTION is used to uploaded file as part of the client request
+ ]"
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ LIBCURL_UPLOAD_FILE_READ_FUNCTION
+
+inherit
+ LIBCURL_DEFAULT_FUNCTION
+ redefine
+ read_function
+ end
+
+create
+ make_with_file
+
+feature {NONE} -- Initialization
+
+ make_with_file (f: FILE)
+ require
+ f_is_open: f.is_open_read
+ do
+ make
+ file_to_read := f
+ end
+
+feature -- Access
+
+ file_to_read: detachable FILE
+ -- File for sending data
+
+feature -- Basic operation
+
+ read_function (a_data_pointer: POINTER; a_size, a_nmemb: INTEGER_32; a_object_id: POINTER): INTEGER_32
+ --
+ local
+ l_pointer: MANAGED_POINTER
+ l_max_transfer, l_byte_transfered: INTEGER
+ do
+ if attached file_to_read as l_file and then not l_file.after then
+ l_max_transfer := a_size * a_nmemb
+ if l_max_transfer > l_file.count - l_file.position then
+ l_max_transfer := l_file.count - l_file.position
+ end
+ create l_pointer.share_from_pointer (a_data_pointer, l_max_transfer)
+
+ from
+ until
+ l_file.after or l_byte_transfered >= l_max_transfer
+ loop
+ l_file.read_character
+ l_pointer.put_character (l_file.last_character, l_byte_transfered)
+
+ l_byte_transfered := l_byte_transfered + 1
+ end
+
+ Result := l_max_transfer
+ else
+ -- Result is 0 means stop file transfer
+ Result := 0
+ end
+ end
+
+
+note
+ copyright: "2011-2012, 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/server/wsf/tests/src/test_wsf_request.e b/library/server/wsf/tests/src/test_wsf_request.e
index 9017133d..fe73d718 100644
--- a/library/server/wsf/tests/src/test_wsf_request.e
+++ b/library/server/wsf/tests/src/test_wsf_request.e
@@ -77,6 +77,7 @@ feature {NONE} -- Events
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
local
q: detachable STRING_32
+ n: NATURAL_64
page: WSF_PAGE_RESPONSE
do
debug
@@ -145,6 +146,15 @@ feature {NONE} -- Events
if not q.is_empty then
page.put_string (" : " + q )
end
+ elseif l_uri.starts_with (test_url ("post/file/01")) then
+ page.put_header (200, <<["Content-Type", "text/plain"]>>)
+ page.put_string ("post-file-01")
+ n := req.content_length_value
+ if n > 0 then
+ req.input.read_string (n.to_integer_32)
+ q := req.input.last_string
+ page.put_string ("%N" + q)
+ end
else
page.put_header (200, <<["Content-Type", "text/plain"]>>)
page.put_string ("Hello")
@@ -191,6 +201,7 @@ feature {NONE} -- Events
if attached {HTTP_CLIENT_SESSION} h.new_session ("localhost:" + port_number.out + "/" + b) as sess then
http_session := sess
sess.set_timeout (-1)
+ sess.set_is_debug (True)
sess.set_connect_timeout (-1)
-- sess.set_proxy ("127.0.0.1", 8888) --| inspect traffic with http://www.fiddler2.com/
end
@@ -220,6 +231,18 @@ feature {NONE} -- Events
end
end
+ test_post_request_with_filename (a_url: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; a_fn: STRING; a_expected_body: READABLE_STRING_8)
+ do
+ get_http_session
+ if attached http_session as sess then
+ if attached sess.post_file (a_url, adapted_context (ctx), a_fn) as res and then not res.error_occurred and then attached res.body as l_body then
+ assert ("Good answer got=%""+l_body+"%" expected=%""+a_expected_body+"%"", l_body.same_string (a_expected_body))
+ else
+ assert ("Request %""+a_url+"%" failed", False)
+ end
+ end
+ end
+
adapted_context (ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_REQUEST_CONTEXT
do
if ctx /= Void then
@@ -246,6 +269,26 @@ feature -- Test routines
end
end
+ test_post_request_uploaded_file
+ -- New test routine
+ local
+ fn: FILE_NAME
+ f: RAW_FILE
+ s: STRING
+ do
+ get_http_session
+ if attached http_session as sess then
+ create fn.make_temporary_name
+ create f.make_create_read_write (fn.string)
+ s := "This is an uploaded file%NTesting purpose%N"
+ f.put_string (s)
+ f.close
+ test_post_request_with_filename ("post/file/01", Void, fn.string, "post-file-01%N" + s)
+ else
+ assert ("not_implemented", False)
+ end
+ end
+
test_post_request_01
-- New test routine
local
diff --git a/library/server/wsf/tests/tests-safe.ecf b/library/server/wsf/tests/tests-safe.ecf
index 2f52e285..6d3bfd74 100644
--- a/library/server/wsf/tests/tests-safe.ecf
+++ b/library/server/wsf/tests/tests-safe.ecf
@@ -7,7 +7,7 @@
/EIFGENs$
/.svn$
-
@@ -15,10 +15,10 @@
+
-