From d06bc7694466231d36a9ae703b70378856ce23ed Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 15 Sep 2011 22:02:58 +0200 Subject: [PATCH 01/14] minor enhancement of error lib --- library/error/src/error_handler.e | 38 ++++++++++++++++++-- library/error/src/error_handler_with_event.e | 11 ++++++ 2 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 library/error/src/error_handler_with_event.e diff --git a/library/error/src/error_handler.e b/library/error/src/error_handler.e index 9234990a..c6e72c8b 100644 --- a/library/error/src/error_handler.e +++ b/library/error/src/error_handler.e @@ -22,6 +22,7 @@ feature {NONE} -- Initialization -- Initialize `Current'. do create {ARRAYED_LIST [ERROR]} errors.make (3) + create error_added_actions end feature -- Status @@ -37,6 +38,8 @@ feature -- Status Result := errors.count end +feature {ERROR_HANDLER, ERROR_VISITOR} -- Restricted access + errors: LIST [ERROR] -- Errors container @@ -52,12 +55,26 @@ feature -- Status report end end +feature -- Events + + error_added_actions: ACTION_SEQUENCE [TUPLE [ERROR]] + -- Actions triggered when a new error is added + +feature {NONE} -- Event: implementation + + on_error_added (e: ERROR) + -- Error `e' was just added + do + error_added_actions.call ([e]) + end + feature -- Basic operation add_error (a_error: ERROR) -- Add `a_error' to the stack of error do errors.force (a_error) + on_error_added (a_error) end add_error_details, add_custom_error (a_code: INTEGER; a_name: STRING; a_message: detachable STRING_32) @@ -69,10 +86,25 @@ feature -- Basic operation add_error (e) end - append (a_err_handler: ERROR_HANDLER) + append (other: ERROR_HANDLER) -- Append errors from `a_err_handler' + local + other_errs: LIST [ERROR] do - errors.append (a_err_handler.errors) + other_errs := other.errors + if other_errs.count > 0 then + from + other_errs.start + until + other_errs.after + loop + add_error (other_errs.item) + other_errs.forth + end + end + ensure + other_error_appended: other.has_error implies has_error + new_count: count = old count + other.count end feature -- Access @@ -107,7 +139,7 @@ feature -- Element changes do if count > 1 and then attached as_single_error as e then wipe_out - add_error (e) + errors.force (e) end end diff --git a/library/error/src/error_handler_with_event.e b/library/error/src/error_handler_with_event.e new file mode 100644 index 00000000..ac69f3ef --- /dev/null +++ b/library/error/src/error_handler_with_event.e @@ -0,0 +1,11 @@ +note + description: "Summary description for {ERROR_HANDLER_WITH_EVENT}." + author: "" + date: "$Date$" + revision: "$Revision$" + + class + ERROR_HANDLER_WITH_EVENT + + end + \ No newline at end of file From 18684d167b667323f89d8b0e621face49965ccb7 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 16 Sep 2011 15:02:08 +0200 Subject: [PATCH 02/14] typo --- library/server/request/rest/src/rest_request_handler.e | 3 ++- library/server/request/rest/tests/sample.ecf | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/library/server/request/rest/src/rest_request_handler.e b/library/server/request/rest/src/rest_request_handler.e index 6f3c8bc6..086db1e6 100644 --- a/library/server/request/rest/src/rest_request_handler.e +++ b/library/server/request/rest/src/rest_request_handler.e @@ -23,7 +23,7 @@ feature -- Access end description: detachable STRING - -- Optional descriptiong + -- Optional description feature -- Element change @@ -83,6 +83,7 @@ feature -- Execution res.write_header ({HTTP_STATUS_CODE}.unauthorized, Void) res.write_string ("Unauthorized") end + feature {NONE} -- Implementation supported_formats: INTEGER diff --git a/library/server/request/rest/tests/sample.ecf b/library/server/request/rest/tests/sample.ecf index 05cfaeab..31ca8469 100644 --- a/library/server/request/rest/tests/sample.ecf +++ b/library/server/request/rest/tests/sample.ecf @@ -18,7 +18,7 @@ - + /gateway$ From 3f899f6aae0b98d79a483e925cfe480ccd9b46ea Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 16 Sep 2011 15:11:37 +0200 Subject: [PATCH 03/14] Added "on_handler_mapped" callback to allow any REQUEST_HANDLER to record the existing routes. --- library/server/request/router/router-safe.ecf | 2 +- library/server/request/router/router.ecf | 2 +- library/server/request/router/src/request_handler.e | 7 +++++++ .../server/request/router/src/uri/request_uri_router_i.e | 1 + .../src/uri_template/request_uri_template_router_i.e | 1 + 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/library/server/request/router/router-safe.ecf b/library/server/request/router/router-safe.ecf index eba5a022..f7aff5b6 100644 --- a/library/server/request/router/router-safe.ecf +++ b/library/server/request/router/router-safe.ecf @@ -24,7 +24,7 @@ /EIFGENs$ /.svn$ - diff --git a/library/server/request/router/router.ecf b/library/server/request/router/router.ecf index b336e855..93fae79f 100644 --- a/library/server/request/router/router.ecf +++ b/library/server/request/router/router.ecf @@ -24,7 +24,7 @@ /EIFGENs$ /.svn$ - diff --git a/library/server/request/router/src/request_handler.e b/library/server/request/router/src/request_handler.e index 30f0fc54..76ba939b 100644 --- a/library/server/request/router/src/request_handler.e +++ b/library/server/request/router/src/request_handler.e @@ -65,6 +65,13 @@ feature -- Execution: report result_attached: Result /= Void end +feature {REQUEST_ROUTER} -- Routes change + + on_handler_mapped (a_resource: READABLE_STRING_8; a_rqst_methods: detachable ARRAY [READABLE_STRING_8]) + -- Callback called when a router map a route to Current handler + do + end + note copyright: "2011-2011, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/library/server/request/router/src/uri/request_uri_router_i.e b/library/server/request/router/src/uri/request_uri_router_i.e index b699f6c0..c82b2089 100644 --- a/library/server/request/router/src/uri/request_uri_router_i.e +++ b/library/server/request/router/src/uri/request_uri_router_i.e @@ -26,6 +26,7 @@ feature -- Registration map_with_request_methods (p: READABLE_STRING_8; h: H; rqst_methods: detachable ARRAY [READABLE_STRING_8]) do handlers.force ([h, p, formatted_request_methods (rqst_methods)]) + h.on_handler_mapped (p, rqst_methods) end feature {NONE} -- Access: Implementation diff --git a/library/server/request/router/src/uri_template/request_uri_template_router_i.e b/library/server/request/router/src/uri_template/request_uri_template_router_i.e index d2f61c4d..2b50ba4d 100644 --- a/library/server/request/router/src/uri_template/request_uri_template_router_i.e +++ b/library/server/request/router/src/uri_template/request_uri_template_router_i.e @@ -33,6 +33,7 @@ feature -- Registration do handlers.force ([h, uri.template, formatted_request_methods (rqst_methods)]) templates.force (uri, uri.template) + h.on_handler_mapped (uri.template, rqst_methods) end map_with_request_methods (tpl: READABLE_STRING_8; h: H; rqst_methods: detachable ARRAY [READABLE_STRING_8]) From c9a4ebcb23548534538083f9d868e0306963adb8 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 16 Sep 2011 15:17:49 +0200 Subject: [PATCH 04/14] added request_handler_routes_recorder to provide an implementation for `REQUEST_HANDLER.on_handler_mapped' --- .../misc/request_handler_routes_recorder.e | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 library/server/request/router/src/misc/request_handler_routes_recorder.e diff --git a/library/server/request/router/src/misc/request_handler_routes_recorder.e b/library/server/request/router/src/misc/request_handler_routes_recorder.e new file mode 100644 index 00000000..80e8be62 --- /dev/null +++ b/library/server/request/router/src/misc/request_handler_routes_recorder.e @@ -0,0 +1,44 @@ +note + description: "[ + Summary description for REQUEST_HANDLER_ROUTES_RECORDER. + + You can inherit from this class from any REQUEST_HANDLER and redefine `on_handler_mapped' + to record the available routes if your handler needs it. + ]" + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + REQUEST_HANDLER_ROUTES_RECORDER + +feature {REQUEST_HANDLER} -- Routes access + + available_routes: detachable LIST [TUPLE [resource: READABLE_STRING_8; rqst_methods: detachable ARRAY [READABLE_STRING_8]]] + -- Available routes + +feature {REQUEST_ROUTER} -- Routes change + + on_handler_mapped (a_resource: READABLE_STRING_8; a_rqst_methods: detachable ARRAY [READABLE_STRING_8]) + local + l_routes: like available_routes + do + l_routes := available_routes + if l_routes = Void then + create {ARRAYED_LIST [like available_routes.item]} l_routes.make (3) + available_routes := l_routes + end + l_routes.force ([a_resource, a_rqst_methods]) + end + +note + copyright: "2011-2011, 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 From 7b1557a52a4e8bdd740917ca8cc121721be9d0be Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 16 Sep 2011 18:53:57 +0200 Subject: [PATCH 05/14] first version of http authorization .. for now, only basic digest --- .../http_authorization-safe.ecf | 16 +++++++ .../http_authorization/http_authorization.ecf | 16 +++++++ .../src/http_authorization.e | 48 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 library/server/authentication/http_authorization/http_authorization-safe.ecf create mode 100644 library/server/authentication/http_authorization/http_authorization.ecf create mode 100644 library/server/authentication/http_authorization/src/http_authorization.e diff --git a/library/server/authentication/http_authorization/http_authorization-safe.ecf b/library/server/authentication/http_authorization/http_authorization-safe.ecf new file mode 100644 index 00000000..247bb953 --- /dev/null +++ b/library/server/authentication/http_authorization/http_authorization-safe.ecf @@ -0,0 +1,16 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + diff --git a/library/server/authentication/http_authorization/http_authorization.ecf b/library/server/authentication/http_authorization/http_authorization.ecf new file mode 100644 index 00000000..20aa415a --- /dev/null +++ b/library/server/authentication/http_authorization/http_authorization.ecf @@ -0,0 +1,16 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + diff --git a/library/server/authentication/http_authorization/src/http_authorization.e b/library/server/authentication/http_authorization/src/http_authorization.e new file mode 100644 index 00000000..f35ea83b --- /dev/null +++ b/library/server/authentication/http_authorization/src/http_authorization.e @@ -0,0 +1,48 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + HTTP_AUTHORIZATION + +create + make + +feature {NONE} -- Initialization + + make (a_http_authorization: detachable READABLE_STRING_GENERAL) + -- Initialize `Current'. + local + p: INTEGER + s: STRING_8 + do + if attached a_http_authorization as l_auth then + s := l_auth.as_string_8 + if not s.is_empty then + p := 1 + if s[p] = ' ' then + p := p + 1 + end + p := s.index_of (' ', p) + if p > 0 then + s := (create {BASE64}).decoded_string (s.substring (p + 1, s.count)) + p := s.index_of (':', 1) --| Let's assume ':' is forbidden in login ... + if p > 0 then + login := s.substring (1, p - 1).as_string_32 + password := s.substring (p + 1, s.count).as_string_32 + end + end + end + end + end + +feature -- Access + + login: detachable READABLE_STRING_32 + + password: detachable READABLE_STRING_32 + + +end From d3239ec41bb864100cc0962d572745ff1d955201 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 16 Sep 2011 18:54:16 +0200 Subject: [PATCH 06/14] added debug_output to WGI_VALUE --- .../server/ewsgi/specification/request/wgi_value.e | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/library/server/ewsgi/specification/request/wgi_value.e b/library/server/ewsgi/specification/request/wgi_value.e index 2919df74..3cb52624 100644 --- a/library/server/ewsgi/specification/request/wgi_value.e +++ b/library/server/ewsgi/specification/request/wgi_value.e @@ -7,6 +7,9 @@ note deferred class WGI_VALUE +inherit + DEBUG_OUTPUT + convert as_string: {READABLE_STRING_32, STRING_32} @@ -29,6 +32,14 @@ feature -- Helper deferred end +feature -- Status report + + debug_output: STRING + -- String that should be displayed in debugger to represent `Current'. + do + create Result.make_from_string (name.as_string_8 + "=" + as_string.as_string_8) + end + feature -- Query as_string: STRING_32 From 64060cfa4113e49c78bd23dbe3b6852c7909f125 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 16 Sep 2011 18:54:44 +0200 Subject: [PATCH 07/14] fixed wrong order in parameter for callers of set_meta_string_variable --- library/server/ewsgi/src/request/wgi_request_from_table.e | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library/server/ewsgi/src/request/wgi_request_from_table.e b/library/server/ewsgi/src/request/wgi_request_from_table.e index 3a819e2a..7025183b 100644 --- a/library/server/ewsgi/src/request/wgi_request_from_table.e +++ b/library/server/ewsgi/src/request/wgi_request_from_table.e @@ -114,13 +114,13 @@ feature {NONE} -- Initialization if attached request_uri as rq_uri then p := rq_uri.index_of ('?', 1) if p > 0 then - set_meta_string_variable (rq_uri.substring (1, p-1), {WGI_META_NAMES}.self) + set_meta_string_variable ({WGI_META_NAMES}.self, rq_uri.substring (1, p-1)) else - set_meta_string_variable (rq_uri, {WGI_META_NAMES}.self) + set_meta_string_variable ({WGI_META_NAMES}.self, rq_uri) end end if meta_variable ({WGI_META_NAMES}.request_time) = Void then - set_meta_string_variable (date_time_utilities.unix_time_stamp (Void).out, {WGI_META_NAMES}.request_time) + set_meta_string_variable ({WGI_META_NAMES}.request_time, date_time_utilities.unix_time_stamp (Void).out) end end @@ -325,6 +325,8 @@ feature -- Access: HTTP_* CGI meta parameters - 1.1 -- Contents of the Host: header from the current request, if there is one. do Result := meta_string_variable ({WGI_META_NAMES}.http_host) + ensure + Result /= Void end http_referer: detachable READABLE_STRING_32 From 92d8357d0933f111342fe8f1e4fc16c4df7a4683 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 16 Sep 2011 18:55:26 +0200 Subject: [PATCH 08/14] more flexible authenticated query .. on handler, and not anymore on context object --- .../server/request/rest/src/rest_request_handler.e | 12 ++++++++++-- .../request/rest/src/rest_request_handler_context.e | 11 ----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/library/server/request/rest/src/rest_request_handler.e b/library/server/request/rest/src/rest_request_handler.e index 086db1e6..1660f996 100644 --- a/library/server/request/rest/src/rest_request_handler.e +++ b/library/server/request/rest/src/rest_request_handler.e @@ -42,7 +42,7 @@ feature -- Execution do if not rescued then if request_method_name_supported (req.request_method) then - if authentication_required (req) and then not ctx.authenticated then + if authentication_required (req) and then not authenticated (ctx) then execute_unauthorized (ctx, req, res) else pre_execute (ctx, req, res) @@ -83,7 +83,15 @@ feature -- Execution res.write_header ({HTTP_STATUS_CODE}.unauthorized, Void) res.write_string ("Unauthorized") end - + +feature -- Auth + + authenticated (ctx: C): BOOLEAN + -- Is authenticated? + do + --| To redefine if needed + end + feature {NONE} -- Implementation supported_formats: INTEGER diff --git a/library/server/request/rest/src/rest_request_handler_context.e b/library/server/request/rest/src/rest_request_handler_context.e index 4fbf9913..8697bca5 100644 --- a/library/server/request/rest/src/rest_request_handler_context.e +++ b/library/server/request/rest/src/rest_request_handler_context.e @@ -54,17 +54,6 @@ feature -- Status report result_attached: Result /= Void end - authenticated: BOOLEAN - do - if request.http_authorization /= Void then - Result := True - end - end - - authenticated_identifier: detachable READABLE_STRING_32 - do - end - note copyright: "Copyright (c) 1984-2011, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" From 111812c4e9292a3243b3af2b9a8695ac65ea1f28 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 16 Sep 2011 18:56:02 +0200 Subject: [PATCH 09/14] Fixed issue with uri template router .. it was applying on request_uri instead of path_info now it match on PATH_INFO --- .../router/src/uri_template/request_uri_template_router_i.e | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/server/request/router/src/uri_template/request_uri_template_router_i.e b/library/server/request/router/src/uri_template/request_uri_template_router_i.e index 2b50ba4d..e43e1935 100644 --- a/library/server/request/router/src/uri_template/request_uri_template_router_i.e +++ b/library/server/request/router/src/uri_template/request_uri_template_router_i.e @@ -54,7 +54,7 @@ feature {NONE} -- Access: Implementation l_req_method: READABLE_STRING_GENERAL l_res: URI_TEMPLATE_MATCH_RESULT do - p := req.request_uri + p := req.path_info from l_req_method := req.request_method l_handlers := handlers From b3ef7c846b8ef215d0e6839a6f00658c0260df24 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 16 Sep 2011 20:59:06 +0200 Subject: [PATCH 10/14] Fixed issues in WGI_REQUEST's invariant Fixed issues with guessing the default format for REST handling Fixed issue with .._ROUTING_.. component. --- .../ewsgi/specification/request/wgi_request.e | 16 +++++++++++++--- .../ewsgi/src/request/wgi_request_from_table.e | 10 ---------- .../src/uri/rest_request_uri_routing_handler_i.e | 2 +- ...rest_request_uri_template_routing_handler_i.e | 2 +- .../request/router/src/request_handler_context.e | 2 ++ 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/library/server/ewsgi/specification/request/wgi_request.e b/library/server/ewsgi/specification/request/wgi_request.e index 75b4d431..2250427c 100644 --- a/library/server/ewsgi/specification/request/wgi_request.e +++ b/library/server/ewsgi/specification/request/wgi_request.e @@ -96,6 +96,16 @@ feature -- Access: CGI meta variables deferred end + meta_string_variable (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 + -- Environment variable related to `a_name' + require + a_name_valid: a_name /= Void and then not a_name.is_empty + do + if attached meta_variable (a_name) as val then + Result := val.as_string + end + end + meta_variables: ITERATION_CURSOR [WGI_VALUE] -- These variables are specific to requests made with HTTP. -- Interpretation of these variables may depend on the value of @@ -687,10 +697,10 @@ invariant query_string_attached: query_string /= Void remote_addr_attached: remote_addr /= Void - same_orig_path_info: orig_path_info ~ meta_variable ({WGI_META_NAMES}.orig_path_info) - same_path_info: path_info ~ meta_variable ({WGI_META_NAMES}.path_info) + same_orig_path_info: orig_path_info ~ meta_string_variable ({WGI_META_NAMES}.orig_path_info) + same_path_info: path_info ~ meta_string_variable ({WGI_META_NAMES}.path_info) - path_info_identical: path_info ~ meta_variable ({WGI_META_NAMES}.path_info) + path_info_identical: path_info ~ meta_string_variable ({WGI_META_NAMES}.path_info) note copyright: "2011-2011, Eiffel Software and others" diff --git a/library/server/ewsgi/src/request/wgi_request_from_table.e b/library/server/ewsgi/src/request/wgi_request_from_table.e index 7025183b..ec6fdb0d 100644 --- a/library/server/ewsgi/src/request/wgi_request_from_table.e +++ b/library/server/ewsgi/src/request/wgi_request_from_table.e @@ -185,14 +185,6 @@ feature -- Access: CGI meta parameters Result := meta_variables_table.item (a_name) end - meta_string_variable (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 - -- CGI meta variable related to `a_name' - do - if attached meta_variables_table.item (a_name) as val then - Result := val.as_string - end - end - meta_string_variable_or_default (a_name: READABLE_STRING_GENERAL; a_default: READABLE_STRING_32; use_default_when_empty: BOOLEAN): READABLE_STRING_32 -- Value for meta parameter `a_name' -- If not found, return `a_default' @@ -325,8 +317,6 @@ feature -- Access: HTTP_* CGI meta parameters - 1.1 -- Contents of the Host: header from the current request, if there is one. do Result := meta_string_variable ({WGI_META_NAMES}.http_host) - ensure - Result /= Void end http_referer: detachable READABLE_STRING_32 diff --git a/library/server/request/rest/src/uri/rest_request_uri_routing_handler_i.e b/library/server/request/rest/src/uri/rest_request_uri_routing_handler_i.e index 86c23462..badbe29d 100644 --- a/library/server/request/rest/src/uri/rest_request_uri_routing_handler_i.e +++ b/library/server/request/rest/src/uri/rest_request_uri_routing_handler_i.e @@ -31,7 +31,7 @@ feature -- Execution execute (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER) do - Precursor {REST_REQUEST_HANDLER} (ctx, req, res) + Precursor {REQUEST_URI_ROUTING_HANDLER_I} (ctx, req, res) end execute_application (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER) diff --git a/library/server/request/rest/src/uri_template/rest_request_uri_template_routing_handler_i.e b/library/server/request/rest/src/uri_template/rest_request_uri_template_routing_handler_i.e index 3cece40b..4dea0a26 100644 --- a/library/server/request/rest/src/uri_template/rest_request_uri_template_routing_handler_i.e +++ b/library/server/request/rest/src/uri_template/rest_request_uri_template_routing_handler_i.e @@ -38,7 +38,7 @@ feature -- Execution execute (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER) do - Precursor {REST_REQUEST_HANDLER} (ctx, req, res) + Precursor {REQUEST_URI_TEMPLATE_ROUTING_HANDLER_I} (ctx, req, res) end execute_application (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER) diff --git a/library/server/request/router/src/request_handler_context.e b/library/server/request/router/src/request_handler_context.e index 1e4f0f4f..bfb16b39 100644 --- a/library/server/request/router/src/request_handler_context.e +++ b/library/server/request/router/src/request_handler_context.e @@ -98,6 +98,8 @@ feature -- Query end i := i + 1 end + else + Result := s end l_accept_lst.forth end From c2f7c198e0a7d752b516e3adc9b2b6ae07eb92fb Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 20 Sep 2011 16:55:44 +0200 Subject: [PATCH 11/14] 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 From dff267cd582cffa364ba5040b1775774c86be218 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 20 Sep 2011 16:57:28 +0200 Subject: [PATCH 12/14] Now using READABLE_STRING_... type --- library/text/encoder/src/encoder.e | 2 +- library/text/encoder/src/url_encoder.e | 102 +++++++++++++------------ 2 files changed, 55 insertions(+), 49 deletions(-) diff --git a/library/text/encoder/src/encoder.e b/library/text/encoder/src/encoder.e index 52cfa047..daec5d7b 100644 --- a/library/text/encoder/src/encoder.e +++ b/library/text/encoder/src/encoder.e @@ -6,7 +6,7 @@ note revision: "$Revision$" deferred class - ENCODER [U -> STRING_GENERAL, E -> STRING_GENERAL] --| U:unencoded type, E:encoded type + ENCODER [U -> READABLE_STRING_GENERAL, E -> READABLE_STRING_GENERAL] --| U:unencoded type, E:encoded type feature -- Access diff --git a/library/text/encoder/src/url_encoder.e b/library/text/encoder/src/url_encoder.e index b5370e49..26a0c832 100644 --- a/library/text/encoder/src/url_encoder.e +++ b/library/text/encoder/src/url_encoder.e @@ -13,7 +13,7 @@ class URL_ENCODER inherit - ENCODER [STRING_32, STRING_8] + ENCODER [READABLE_STRING_32, READABLE_STRING_8] PLATFORM export @@ -30,48 +30,17 @@ feature -- Status report feature -- Encoder - encoded_string (s: STRING_32): STRING_8 + encoded_string (s: READABLE_STRING_32): READABLE_STRING_8 -- URL-encoded value of `s'. local i, n: INTEGER uc: CHARACTER_32 c: CHARACTER_8 + s8: STRING_8 do has_error := False - create Result.make (s.count + s.count // 10) - n := s.count - from i := 1 until i > n loop - uc := s.item (i) - if uc.is_character_8 then - c := uc.to_character_8 - inspect c - when - 'A' .. 'Z', - 'a' .. 'z', '0' .. '9', - '.', '-', '~', '_' - then - Result.extend (c) - when ' ' then - Result.extend ('+') - else - Result.append (url_encoded_char (uc)) - end - else - Result.append (url_encoded_char (uc)) - end - i := i + 1 - end - end - - partial_encoded_string (s: STRING_32; a_ignore: ARRAY [CHARACTER]): STRING_8 - -- URL-encoded value of `s'. - local - i, n: INTEGER - uc: CHARACTER_32 - c: CHARACTER_8 - do - has_error := False - create Result.make (s.count + s.count // 10) + create s8.make (s.count + s.count // 10) + Result := s8 n := s.count from i := 1 until i > n loop uc := s.item (i) @@ -83,21 +52,56 @@ feature -- Encoder 'a' .. 'z', '0' .. '9', '.', '-', '~', '_' then - Result.extend (c) + s8.extend (c) when ' ' then - Result.extend ('+') + s8.extend ('+') + else + s8.append (url_encoded_char (uc)) + end + else + s8.append (url_encoded_char (uc)) + end + i := i + 1 + end + end + + partial_encoded_string (s: READABLE_STRING_32; a_ignore: ARRAY [CHARACTER]): READABLE_STRING_8 + -- URL-encoded value of `s'. + local + i, n: INTEGER + uc: CHARACTER_32 + c: CHARACTER_8 + s8: STRING_8 + do + has_error := False + create s8.make (s.count + s.count // 10) + Result := s8 + n := s.count + from i := 1 until i > n loop + uc := s.item (i) + if uc.is_character_8 then + c := uc.to_character_8 + inspect c + when + 'A' .. 'Z', + 'a' .. 'z', '0' .. '9', + '.', '-', '~', '_' + then + s8.extend (c) + when ' ' then + s8.extend ('+') else if a_ignore.has (c) then - Result.extend (c) + s8.extend (c) else - Result.append (url_encoded_char (uc)) + s8.append (url_encoded_char (uc)) end end else if a_ignore.has (c) then - Result.extend (c) + s8.extend (c) else - Result.append (url_encoded_char (uc)) + s8.append (url_encoded_char (uc)) end end i := i + 1 @@ -127,33 +131,35 @@ feature {NONE} -- encoder character feature -- Decoder - decoded_string (v: STRING_8): STRING_32 + decoded_string (v: READABLE_STRING_8): READABLE_STRING_32 -- The URL-encoded equivalent of the given string local i, n: INTEGER c: CHARACTER pr: CELL [INTEGER] + s32: STRING_32 do n := v.count - create Result.make (n) + create s32.make (n) + Result := s32 from i := 1 until i > n loop c := v.item (i) inspect c when '+' then - Result.append_character ({CHARACTER_32}' ') + s32.append_character ({CHARACTER_32}' ') when '%%' then -- An escaped character ? if i = n then - Result.append_character (c.to_character_32) + s32.append_character (c.to_character_32) else create pr.put (i) - Result.append (url_decoded_char (v, pr)) + s32.append (url_decoded_char (v, pr)) i := pr.item end else - Result.append_character (c.to_character_32) + s32.append_character (c.to_character_32) end i := i + 1 end From 284d7826c8bac6fb8b076c0f76563159feb367df Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 20 Sep 2011 16:59:54 +0200 Subject: [PATCH 13/14] missing -safe.ecf config file for http_client --- .../client/http_client/http_client-safe.ecf | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 library/client/http_client/http_client-safe.ecf diff --git a/library/client/http_client/http_client-safe.ecf b/library/client/http_client/http_client-safe.ecf new file mode 100644 index 00000000..1ce4a9d3 --- /dev/null +++ b/library/client/http_client/http_client-safe.ecf @@ -0,0 +1,36 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + /request$ + + + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + From 0414cd45012087faec9cee9e4040179f367404e4 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 20 Sep 2011 18:19:23 +0200 Subject: [PATCH 14/14] fixed case sensitive path --- library/client/http_client/http_client-safe.ecf | 2 +- library/client/http_client/http_client.ecf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/client/http_client/http_client-safe.ecf b/library/client/http_client/http_client-safe.ecf index 1ce4a9d3..e3a15665 100644 --- a/library/client/http_client/http_client-safe.ecf +++ b/library/client/http_client/http_client-safe.ecf @@ -10,7 +10,7 @@ - + diff --git a/library/client/http_client/http_client.ecf b/library/client/http_client/http_client.ecf index 15ab07a9..0240a509 100644 --- a/library/client/http_client/http_client.ecf +++ b/library/client/http_client/http_client.ecf @@ -10,7 +10,7 @@ - +