From c2f7c198e0a7d752b516e3adc9b2b6ae07eb92fb Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 20 Sep 2011 16:55:44 +0200 Subject: [PATCH] Added simple HTTP client. For now the implementation is using Eiffel cURL library. It requires Eiffel cURL coming with next EiffelStudio 7.0 (or from eiffelstudio's repo from rev#87244 ) --- library/client/http_client/http_client.ecf | 21 ++ library/client/http_client/src/http_client.e | 16 ++ .../http_client/src/http_client_constants.e | 37 ++++ .../http_client/src/http_client_request.e | 183 +++++++++++++++++ .../src/http_client_request_context.e | 48 +++++ .../http_client/src/http_client_response.e | 60 ++++++ .../http_client/src/http_client_session.e | 163 +++++++++++++++ .../src/spec/libcurl/libcurl_http_client.e | 30 +++ .../libcurl/libcurl_http_client_request.e | 192 ++++++++++++++++++ .../libcurl/libcurl_http_client_session.e | 85 ++++++++ 10 files changed, 835 insertions(+) create mode 100644 library/client/http_client/http_client.ecf create mode 100644 library/client/http_client/src/http_client.e create mode 100644 library/client/http_client/src/http_client_constants.e create mode 100644 library/client/http_client/src/http_client_request.e create mode 100644 library/client/http_client/src/http_client_request_context.e create mode 100644 library/client/http_client/src/http_client_response.e create mode 100644 library/client/http_client/src/http_client_session.e create mode 100644 library/client/http_client/src/spec/libcurl/libcurl_http_client.e create mode 100644 library/client/http_client/src/spec/libcurl/libcurl_http_client_request.e create mode 100644 library/client/http_client/src/spec/libcurl/libcurl_http_client_session.e diff --git a/library/client/http_client/http_client.ecf b/library/client/http_client/http_client.ecf new file mode 100644 index 00000000..15ab07a9 --- /dev/null +++ b/library/client/http_client/http_client.ecf @@ -0,0 +1,21 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + /request$ + + + + diff --git a/library/client/http_client/src/http_client.e b/library/client/http_client/src/http_client.e new file mode 100644 index 00000000..f415811f --- /dev/null +++ b/library/client/http_client/src/http_client.e @@ -0,0 +1,16 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +deferred class + HTTP_CLIENT + +feature -- Status + + new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION + deferred + end + +end diff --git a/library/client/http_client/src/http_client_constants.e b/library/client/http_client/src/http_client_constants.e new file mode 100644 index 00000000..7586451c --- /dev/null +++ b/library/client/http_client/src/http_client_constants.e @@ -0,0 +1,37 @@ +note + description: "Summary description for {HTTP_CLIENT_CONSTANTS}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_CLIENT_CONSTANTS + +feature -- Auth type + + auth_type_id (a_auth_type_string: READABLE_STRING_8): INTEGER + local + s: STRING_8 + do + create s.make_from_string (a_auth_type_string) + s.to_lower + if s.same_string_general ("basic") then + Result := Auth_type_basic + elseif s.same_string_general ("digest") then + Result := Auth_type_digest + elseif s.same_string_general ("any") then + Result := Auth_type_any + elseif s.same_string_general ("anysafe") then + Result := Auth_type_anysafe + elseif s.same_string_general ("none") then + Result := Auth_type_none + end + end + + Auth_type_none: INTEGER = 0 + Auth_type_basic: INTEGER = 1 + Auth_type_digest: INTEGER = 2 + Auth_type_any: INTEGER = 3 + Auth_type_anysafe: INTEGER = 4 + +end diff --git a/library/client/http_client/src/http_client_request.e b/library/client/http_client/src/http_client_request.e new file mode 100644 index 00000000..edb36b5d --- /dev/null +++ b/library/client/http_client/src/http_client_request.e @@ -0,0 +1,183 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +deferred class + HTTP_CLIENT_REQUEST + +inherit + REFACTORING_HELPER + +feature {NONE} -- Initialization + + make (a_url: READABLE_STRING_8; a_session: like session) + -- Initialize `Current'. + do + session := a_session + url := a_url + headers := session.headers.twin + end + + session: HTTP_CLIENT_SESSION + +feature -- Access + + request_method: READABLE_STRING_8 + deferred + end + + url: READABLE_STRING_8 + + headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] + +feature -- Execution + + import (ctx: HTTP_CLIENT_REQUEST_CONTEXT) + do + headers.fill (ctx.headers) + end + + execute (ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + deferred + end + +feature -- Authentication + + auth_type: STRING + -- Set the authentication type for the request. + -- Types: "basic", "digest", "any" + do + Result := session.auth_type + end + + auth_type_id: INTEGER + -- Set the authentication type for the request. + -- Types: "basic", "digest", "any" + do + Result := session.auth_type_id + end + + username: detachable READABLE_STRING_8 + do + Result := session.username + end + + password: detachable READABLE_STRING_8 + do + Result := session.password + end + + credentials: detachable READABLE_STRING_8 + do + Result := session.credentials + end + +feature -- Settings + + timeout: INTEGER + -- HTTP transaction timeout in seconds. + do + Result := session.timeout + end + + connect_timeout: INTEGER + -- HTTP connection timeout in seconds. + do + Result := session.connect_timeout + end + + max_redirects: INTEGER + -- Maximum number of times to follow redirects. + do + Result := session.max_redirects + end + + ignore_content_length: BOOLEAN + -- Does this session ignore Content-Size headers? + do + Result := session.ignore_content_length + end + + buffer_size: NATURAL + -- Set the buffer size for request. This option will + -- only be set if buffer_size is positive + do + Result := session.buffer_size + end + + default_response_charset: detachable READABLE_STRING_8 + -- Default encoding of responses. Used if no charset is provided by the host. + do + Result := session.default_response_charset + end + +feature {NONE} -- Utilities + + append_parameters_to_url (a_url: STRING; a_parameters: detachable ARRAY [detachable TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) + -- Append parameters `a_parameters' to `a_url' + require + a_url_attached: a_url /= Void + local + i: INTEGER + l_first_param: BOOLEAN + do + if a_parameters /= Void and then a_parameters.count > 0 then + if a_url.index_of ('?', 1) > 0 then + l_first_param := False + elseif a_url.index_of ('&', 1) > 0 then + l_first_param := False + else + l_first_param := True + end + from + i := a_parameters.lower + until + i > a_parameters.upper + loop + if attached a_parameters[i] as a_param then + if l_first_param then + a_url.append_character ('?') + else + a_url.append_character ('&') + end + a_url.append_string (a_param.name) + a_url.append_character ('=') + a_url.append_string (a_param.value) + l_first_param := False + end + i := i + 1 + end + end + end + +feature {NONE} -- Utilities: encoding + + url_encoder: URL_ENCODER + once + create Result + end + + urlencode (s: READABLE_STRING_32): READABLE_STRING_8 + -- URL encode `s' + do + Result := url_encoder.encoded_string (s) + end + + urldecode (s: READABLE_STRING_8): READABLE_STRING_32 + -- URL decode `s' + do + Result := url_encoder.decoded_string (s) + end + + stripslashes (s: STRING): STRING + do + Result := s.string + Result.replace_substring_all ("\%"", "%"") + Result.replace_substring_all ("\'", "'") + Result.replace_substring_all ("\/", "/") + Result.replace_substring_all ("\\", "\") + end + +end diff --git a/library/client/http_client/src/http_client_request_context.e b/library/client/http_client/src/http_client_request_context.e new file mode 100644 index 00000000..8ffd0e9d --- /dev/null +++ b/library/client/http_client/src/http_client_request_context.e @@ -0,0 +1,48 @@ +note + description: "Summary description for {HTTP_CLIENT_REQUEST_CONTEXT}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_CLIENT_REQUEST_CONTEXT + +create + make + +feature {NONE} -- Initialization + + make + do + create headers.make (2) + create query_parameters.make (5) + create form_data_parameters.make (10) + end + +feature -- Settings + + credentials_required: BOOLEAN + +feature -- Access + + headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] + + query_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_8] + + form_data_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_8] + +feature -- Status report + + has_form_data: BOOLEAN + do + Result := not form_data_parameters.is_empty + end + +feature -- Element change + + set_credentials_required (b: BOOLEAN) + do + credentials_required := b + end + +end diff --git a/library/client/http_client/src/http_client_response.e b/library/client/http_client/src/http_client_response.e new file mode 100644 index 00000000..c7f46d28 --- /dev/null +++ b/library/client/http_client/src/http_client_response.e @@ -0,0 +1,60 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + HTTP_CLIENT_RESPONSE + +create + make + +feature {NONE} -- Initialization + + make + -- Initialize `Current'. + do + status := 200 + raw_headers := "" + end + +feature -- Status + +feature -- Access + + status: INTEGER assign set_status + + raw_headers: READABLE_STRING_8 + + headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] + local + tb: like internal_headers + do + tb := internal_headers + if tb = Void then + create tb.make (3) + internal_headers := tb + end + Result := tb + end + + body: detachable READABLE_STRING_8 assign set_body + +feature -- Change + + set_status (s: INTEGER) + do + status := s + end + + set_body (s: like body) + do + body := s + end + +feature {NONE} -- Implementation + + internal_headers: detachable like headers + +end diff --git a/library/client/http_client/src/http_client_session.e b/library/client/http_client/src/http_client_session.e new file mode 100644 index 00000000..964958f5 --- /dev/null +++ b/library/client/http_client/src/http_client_session.e @@ -0,0 +1,163 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +deferred class + HTTP_CLIENT_SESSION + +feature {NONE} -- Initialization + + make (a_base_url: READABLE_STRING_8) + -- Initialize `Current'. + do + set_defaults + create headers.make (3) + + base_url := a_base_url + initialize + end + + set_defaults + do + timeout := 5 + connect_timeout := 1 + max_redirects := 5 + set_basic_auth_type + end + + initialize + deferred + end + +feature -- Basic operation + + get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + deferred + end + + head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + deferred + end + + post (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + deferred + end + + put (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + deferred + end + + delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + deferred + end + +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. + + max_redirects: INTEGER + -- Maximum number of times to follow redirects. + -- Set to 0 to disable and -1 to follow all redirects. Defaults to 5. + + ignore_content_length: BOOLEAN + -- Does this session ignore Content-Size headers? + + buffer_size: NATURAL + -- Set the buffer size for request. This option will + -- only be set if buffer_size is positive + + default_response_charset: detachable READABLE_STRING_8 + -- Default encoding of responses. Used if no charset is provided by the host. + +feature -- Access + + base_url: READABLE_STRING_8 + + headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] + +feature -- Authentication + + auth_type: STRING + -- Set the authentication type for the request. + -- Types: "basic", "digest", "any" + + auth_type_id: INTEGER + -- See {HTTP_CLIENT_CONSTANTS}.Auth_type_* + + username, + password: detachable READABLE_STRING_8 + + credentials: detachable READABLE_STRING_8 + +feature -- Change + + set_timeout (n: like timeout) + do + timeout := n + end + + set_user_agent (v: READABLE_STRING_8) + do + add_header ("User-Agent", v) + end + + add_header (k: READABLE_STRING_8; v: READABLE_STRING_8) + do + headers.force (v, k) + end + + set_credentials (u: like username; p: like password) + do + username := u + password := p + if u /= Void and p /= Void then + credentials := u + ":" + p + else + credentials := Void + end + end + + set_auth_type (s: READABLE_STRING_8) + do + auth_type := s + auth_type_id := http_client_constants.auth_type_id (s) + end + + set_basic_auth_type + do + auth_type := "basic" + auth_type_id := {HTTP_CLIENT_CONSTANTS}.auth_type_basic + end + + set_digest_auth_type + do + auth_type := "digest" + auth_type_id := {HTTP_CLIENT_CONSTANTS}.auth_type_digest + end + + set_any_auth_type + do + auth_type := "any" + auth_type_id := {HTTP_CLIENT_CONSTANTS}.auth_type_any + end + + set_anysafe_auth_type + do + auth_type := "anysafe" + auth_type_id := {HTTP_CLIENT_CONSTANTS}.auth_type_anysafe + end + +feature {NONE} -- Implementation + + http_client_constants: HTTP_CLIENT_CONSTANTS + once + create Result + end +end diff --git a/library/client/http_client/src/spec/libcurl/libcurl_http_client.e b/library/client/http_client/src/spec/libcurl/libcurl_http_client.e new file mode 100644 index 00000000..560fdf40 --- /dev/null +++ b/library/client/http_client/src/spec/libcurl/libcurl_http_client.e @@ -0,0 +1,30 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + LIBCURL_HTTP_CLIENT + +inherit + HTTP_CLIENT + +create + make + +feature {NONE} -- Initialization + + make + -- Initialize `Current'. + do + end + +feature -- Status + + new_session (a_base_url: READABLE_STRING_8): LIBCURL_HTTP_CLIENT_SESSION + do + create Result.make (a_base_url) + end + +end 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 new file mode 100644 index 00000000..a6ec2f1a --- /dev/null +++ b/library/client/http_client/src/spec/libcurl/libcurl_http_client_request.e @@ -0,0 +1,192 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + LIBCURL_HTTP_CLIENT_REQUEST + +inherit + HTTP_CLIENT_REQUEST + rename + make as make_request + redefine + session + end + + +create + make + +feature {NONE} -- Initialization + + make (a_url: READABLE_STRING_8; a_request_method: like request_method; a_session: like session) + do + make_request (a_url, a_session) + request_method := a_request_method + end + + session: LIBCURL_HTTP_CLIENT_SESSION + +feature -- Access + + request_method: READABLE_STRING_8 + +feature -- Execution + + execute (ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + local + l_result: INTEGER + l_curl_string: CURL_STRING + l_url: READABLE_STRING_8 + p: POINTER + a_data: CELL [detachable ANY] + l_form, l_last: CURL_FORM + curl: CURL_EXTERNALS + curl_easy: CURL_EASY_EXTERNALS + curl_handle: POINTER + do + curl := session.curl + curl_easy := session.curl_easy + + 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 + + --| Configure cURL session + curl_handle := curl_easy.init + + --| URL + curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_url, l_url) + + --| Timeout + if timeout > 0 then + curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_timeout, timeout) + end + --| Connect Timeout + if connect_timeout > 0 then + curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_connecttimeout, timeout) + end + --| Redirection + if max_redirects /= 0 then + curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_followlocation, 1) + curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_maxredirs, max_redirects) + else + curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_followlocation, 0) + end + + 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 + curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_post, 1) + elseif request_method.is_case_insensitive_equal ("PUT") then + curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_put, 1) + elseif request_method.is_case_insensitive_equal ("HEAD") then + curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_nobody, 1) + elseif request_method.is_case_insensitive_equal ("DELETE") then + curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_customrequest, "DELETE") + else + 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_data_parameters as l_posts and then not l_posts.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_posts.start + until + l_posts.after + loop + curl.formadd_string_string (l_form, l_last, {CURL_FORM_CONSTANTS}.CURLFORM_COPYNAME, l_posts.key_for_iteration, {CURL_FORM_CONSTANTS}.CURLFORM_COPYCONTENTS, l_posts.item_for_iteration, {CURL_FORM_CONSTANTS}.CURLFORM_END) + l_posts.forth + end + l_last.release_item + curl_easy.setopt_form (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httppost, l_form) + 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) +-- curl_easy.setopt_slist (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpheader, p) + 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) + + create a_data.put (Void) + l_result := curl_easy.getinfo (curl_handle, {CURL_INFO_CONSTANTS}.curlinfo_response_code, a_data) + if l_result = 0 and then attached {INTEGER} a_data.item as l_http_status then + Result.status := l_http_status + else + Result.status := 0 + end + +-- last_api_call := l_url + curl_easy.cleanup (curl_handle) + + + Result.body := l_curl_string.string + end + +end diff --git a/library/client/http_client/src/spec/libcurl/libcurl_http_client_session.e b/library/client/http_client/src/spec/libcurl/libcurl_http_client_session.e new file mode 100644 index 00000000..740f4e4e --- /dev/null +++ b/library/client/http_client/src/spec/libcurl/libcurl_http_client_session.e @@ -0,0 +1,85 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + LIBCURL_HTTP_CLIENT_SESSION + +inherit + HTTP_CLIENT_SESSION + +create + make + +feature {NONE} -- Initialization + + initialize + do + create curl -- cURL externals + create curl_easy -- cURL easy externals + end + +feature -- Basic operation + + get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + local + req: HTTP_CLIENT_REQUEST + do + create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "GET", Current) + Result := execute_request (req, ctx) + end + + head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + local + req: HTTP_CLIENT_REQUEST + do + create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "HEAD", Current) + Result := execute_request (req, ctx) + end + + post (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + local + req: HTTP_CLIENT_REQUEST + do + create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "POST", Current) + Result := execute_request (req, ctx) + end + + put (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + local + req: HTTP_CLIENT_REQUEST + do + create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "PUT", Current) + Result := execute_request (req, ctx) + end + + delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + local + req: HTTP_CLIENT_REQUEST + do + create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "DELETE", Current) + Result := execute_request (req, ctx) + end + +feature {NONE} -- Implementation + + execute_request (req: HTTP_CLIENT_REQUEST; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + do + if ctx /= Void then + req.import (ctx) + end + Result := req.execute (ctx) + end + +feature {LIBCURL_HTTP_CLIENT_REQUEST} -- Curl implementation + + curl: CURL_EXTERNALS + -- cURL externals + + curl_easy: CURL_EASY_EXTERNALS + -- cURL easy externals + + +end