From 91fbb3866ff04e95da69cd47537be8b0da1d7292 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 18 Jun 2015 14:49:29 +0200 Subject: [PATCH] Experimentation of WSF_EXECUTOR.execute (req, res) --- .../router/filter/wsf_filtered_execution.e | 14 +- .../wsf/router/filter/wsf_filtered_executor.e | 43 +++ .../execution/wsf_routed_skeleton_executor.e | 281 ++++++++++++++++++ .../server/wsf/router/wsf_routed_execution.e | 64 +--- .../server/wsf/router/wsf_routed_executor.e | 91 ++++++ library/server/wsf/src/wsf_execution.e | 6 +- library/server/wsf/src/wsf_executor.e | 31 ++ 7 files changed, 467 insertions(+), 63 deletions(-) create mode 100644 library/server/wsf/router/filter/wsf_filtered_executor.e create mode 100644 library/server/wsf/router/policy/execution/wsf_routed_skeleton_executor.e create mode 100644 library/server/wsf/router/wsf_routed_executor.e create mode 100644 library/server/wsf/src/wsf_executor.e diff --git a/library/server/wsf/router/filter/wsf_filtered_execution.e b/library/server/wsf/router/filter/wsf_filtered_execution.e index 3de16a50..fb52ea34 100644 --- a/library/server/wsf/router/filter/wsf_filtered_execution.e +++ b/library/server/wsf/router/filter/wsf_filtered_execution.e @@ -13,21 +13,27 @@ inherit initialize end - WSF_FILTERED + WSF_FILTERED_EXECUTOR + rename + initialize as initialize_executor, + execute as process_execution + end feature {NONE} -- Initialization initialize do - Precursor - initialize_filter + Precursor {WSF_EXECUTION} + initialize_executor end feature -- Execution execute + -- Dispatch the request + -- and if handler is not found, execute the default procedure `execute_default'. do - filter.execute (request, response) + process_execution (request, response) end ;note diff --git a/library/server/wsf/router/filter/wsf_filtered_executor.e b/library/server/wsf/router/filter/wsf_filtered_executor.e new file mode 100644 index 00000000..2196c4f0 --- /dev/null +++ b/library/server/wsf/router/filter/wsf_filtered_executor.e @@ -0,0 +1,43 @@ +note + description: "Summary description for {WSF_FILTERED_EXECUTOR}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + WSF_FILTERED_EXECUTOR + +inherit + WSF_EXECUTOR + redefine + initialize + end + + WSF_FILTERED + +feature {NONE} -- Initialization + + initialize + do + Precursor + initialize_filter + end + +feature -- Execution + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + do + filter.execute (req, res) + end + +;note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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/router/policy/execution/wsf_routed_skeleton_executor.e b/library/server/wsf/router/policy/execution/wsf_routed_skeleton_executor.e new file mode 100644 index 00000000..a4fba877 --- /dev/null +++ b/library/server/wsf/router/policy/execution/wsf_routed_skeleton_executor.e @@ -0,0 +1,281 @@ +note + description: "Skeleton execution based on router." + date: "$Date$" + revision: "$Revision$" + +deferred class + WSF_ROUTED_SKELETON_EXECUTOR + +inherit + WSF_ROUTED_EXECUTOR + redefine + execute + end + + WSF_SYSTEM_OPTIONS_ACCESS_POLICY + + WSF_PROXY_USE_POLICY + +feature -- Execution + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- If the service is available, and request URI is not too long, dispatch the request + -- and if handler is not found, execute the default procedure `execute_default'. + do + --| When we reach here, the request has already passed check for 400 (Bad request), + --| which is implemented in WSF_REQUEST.make_from_wgi (when it calls `analyze'). + if unavailable then + handle_unavailable (res) + elseif requires_proxy (req) then + handle_use_proxy (req, res) + elseif + maximum_uri_length > 0 and then + req.request_uri.count.to_natural_32 > maximum_uri_length + then + handle_request_uri_too_long (res) + elseif + req.is_request_method ({HTTP_REQUEST_METHODS}.method_options) and then + req.request_uri.same_string ("*") + then + handle_server_options (req, res) + else + Precursor (req, res) + end + end + +feature -- Measurement + + maximum_uri_length: NATURAL + -- Maximum length in characters (or zero for no limit) permitted + -- for {WSF_REQUEST}.request_uri + +feature -- Status report + + unavailable: BOOLEAN + -- Is service currently unavailable? + + unavailablity_message: detachable READABLE_STRING_8 + -- Message to be included as text of response body for {HTTP_STATUS_CODE}.service_unavailable + + unavailability_duration: NATURAL + -- Delta seconds for service unavailability (0 if not known) + + unavailable_until: detachable DATE_TIME + -- Time at which service becomes available again (if known) + +feature -- Status setting + + set_available + -- Set `unavailable' to `False'. + do + unavailable := False + unavailablity_message := Void + unavailable_until := Void + ensure + available: unavailable = False + unavailablity_message_detached: unavailablity_message = Void + unavailable_until_detached: unavailable_until = Void + end + + set_unavailable (a_message: READABLE_STRING_8; a_duration: NATURAL; a_until: detachable DATE_TIME) + -- Set `unavailable' to `True'. + require + a_message_attached: a_message /= Void + a_duration_xor_a_until: a_duration > 0 implies a_until = Void + do + unavailable := True + unavailablity_message := a_message + unavailability_duration := a_duration + ensure + unavailable: unavailable = True + unavailablity_message_aliased: unavailablity_message = a_message + unavailability_duration_set: unavailability_duration = a_duration + unavailable_until_aliased: unavailable_until = a_until + end + + set_maximum_uri_length (a_len: NATURAL) + -- Set `maximum_uri_length' to `a_len'. + -- Can pass zero to mean no restrictions. + do + maximum_uri_length := a_len + ensure + maximum_uri_length_set: maximum_uri_length = a_len + end + +feature {NONE} -- Implementation + + handle_unavailable (res: WSF_RESPONSE) + -- Write "Service unavailable" response to `res'. + require + unavailable: unavailable + res_attached: res /= Void + local + h: HTTP_HEADER + do + create h.make + h.put_content_type_text_plain + check attached unavailablity_message as m then + -- invariant `unavailability_message_attached' plus precondition `unavailable' + h.put_content_length (m.count) + h.put_current_date + res.set_status_code ({HTTP_STATUS_CODE}.service_unavailable) + if unavailability_duration > 0 then + h.put_header_key_value ({HTTP_HEADER_NAMES}.header_retry_after, unavailability_duration.out) + elseif attached unavailable_until as u then + h.put_header_key_value ({HTTP_HEADER_NAMES}.header_retry_after, + h.date_to_rfc1123_http_date_format (u)) + end + res.put_header_text (h.string) + res.put_string (m) + end + ensure + response_status_is_set: res.status_is_set + status_is_service_unavailable: res.status_code = {HTTP_STATUS_CODE}.service_unavailable + body_sent: res.message_committed and then res.transfered_content_length > 0 + body_content_was_unavailablity_message: True -- doesn't seem to be any way to check + end + + handle_request_uri_too_long (res: WSF_RESPONSE) + -- Write "Request URI too long" response into `res'. + require + res_attached: res /= Void + local + h: HTTP_HEADER + m: READABLE_STRING_8 + do + create h.make + h.put_content_type_text_plain + h.put_current_date + m := "Maximum permitted length for request URI is " + maximum_uri_length.out + " characters" + h.put_content_length (m.count) + res.set_status_code ({HTTP_STATUS_CODE}.request_uri_too_long) + res.put_header_text (h.string) + res.put_string (m) + ensure + response_status_is_set: res.status_is_set + status_is_request_uri_too_long: res.status_code = {HTTP_STATUS_CODE}.request_uri_too_long + body_sent: res.message_committed and then res.transfered_content_length > 0 + end + + frozen handle_server_options (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Write response to OPTIONS * into `res'. + require + req_attached: req /= Void + res_attached: res /= Void + method_is_options: req.is_request_method ({HTTP_REQUEST_METHODS}.method_options) + server_options_requested: req.request_uri.same_string ("*") + do + --| First check if forbidden. + --| (N.B. authentication requires an absoluteURI (RFC3617 page 3), and so cannot be used for OPTIONS *. + --| Otherwise construct an Allow response automatically from the router. + if is_system_options_forbidden (req) then + handle_system_options_forbidden (req, res) + else + handle_system_options (req, res) + end + ensure + response_status_is_set: res.status_is_set + valid_response_code: res.status_code = {HTTP_STATUS_CODE}.forbidden or + res.status_code = {HTTP_STATUS_CODE}.not_found or res.status_code = {HTTP_STATUS_CODE}.ok + header_sent: res.header_committed and res.message_committed + end + + frozen handle_system_options_forbidden (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Write a 403 Forbidden or a 404 Not found response into `res'. + require + req_attached: req /= Void + res_attached: res /= Void + method_is_options: req.is_request_method ({HTTP_REQUEST_METHODS}.method_options) + server_options_requested: req.request_uri.same_string ("*") + local + m: detachable READABLE_STRING_8 + h: HTTP_HEADER + do + m := system_options_forbidden_text (req) + if attached {READABLE_STRING_8} m as l_msg then + create h.make + h.put_content_type_text_plain + h.put_current_date + h.put_content_length (l_msg.count) + res.set_status_code ({HTTP_STATUS_CODE}.forbidden) + res.put_header_text (h.string) + res.put_string (l_msg) + else + create h.make + h.put_content_type_text_plain + h.put_current_date + h.put_content_length (0) + res.set_status_code ({HTTP_STATUS_CODE}.not_found) + res.put_header_text (h.string) + end + ensure + response_status_is_set: res.status_is_set + valid_response_code: res.status_code = {HTTP_STATUS_CODE}.forbidden or + res.status_code = {HTTP_STATUS_CODE}.not_found + header_sent: res.header_committed + message_sent: res.message_committed + end + + handle_system_options (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Write response to OPTIONS * into `res'. + -- This may be redefined by the user, but normally this will not be necessary. + require + req_attached: req /= Void + res_attached: res /= Void + method_is_options: req.is_request_method ({HTTP_REQUEST_METHODS}.method_options) + server_options_requested: req.request_uri.same_string ("*") + local + h: HTTP_HEADER + do + create h.make + h.put_content_type_text_plain + h.put_current_date + h.put_allow (router.all_allowed_methods) + h.put_content_length (0) + res.set_status_code ({HTTP_STATUS_CODE}.ok) + res.put_header_text (h.string) + ensure + response_status_is_set: res.status_is_set + response_code_ok: res.status_code = {HTTP_STATUS_CODE}.ok + header_sent: res.header_committed and res.message_committed + empty_body: res.transfered_content_length = 0 + end + + frozen handle_use_proxy (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Write Use Proxy response `res'. + require + res_attached: res /= Void + req_attached: req /= Void + proxy_required: requires_proxy (req) + local + h: HTTP_HEADER + do + create h.make + h.put_content_type_text_plain + h.put_current_date + h.put_location (proxy_server (req).string) + h.put_content_length (0) + res.put_header_lines (h) + res.set_status_code ({HTTP_STATUS_CODE}.use_proxy) + ensure + response_status_is_set: res.status_is_set + response_code_use_proxy: res.status_code = {HTTP_STATUS_CODE}.use_proxy + end + +invariant + + unavailability_message_attached: unavailable implies attached unavailablity_message as m and then + m.count > 0 + unavailability_duration_xor_unavailable_until: unavailability_duration > 0 implies unavailable_until = Void + +;note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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/router/wsf_routed_execution.e b/library/server/wsf/router/wsf_routed_execution.e index cd67f53f..831da31d 100644 --- a/library/server/wsf/router/wsf_routed_execution.e +++ b/library/server/wsf/router/wsf_routed_execution.e @@ -12,75 +12,27 @@ inherit initialize end - WSF_ROUTED + WSF_ROUTED_EXECUTOR + rename + initialize as initialize_executor, + execute as process_execution + end feature {NONE} -- Initialize initialize do - Precursor - initialize_router + Precursor {WSF_EXECUTION} + initialize_executor end -feature -- Router - - initialize_router - -- Initialize router - do - create_router - setup_router - end - - create_router - -- Create `router' - --| could be redefine to initialize with proper capacity - do - create router.make (10) - ensure - router_created: router /= Void - end - - setup_router - -- Setup `router' - require - router_created: router /= Void - deferred - end - -feature -- Access - - router: WSF_ROUTER - -- Router used to dispatch the request according to the WSF_REQUEST object - -- and associated request methods - feature -- Execution execute -- Dispatch the request -- and if handler is not found, execute the default procedure `execute_default'. do - router_execute (request, response) - end - - router_execute (req: WSF_REQUEST; res: WSF_RESPONSE) - local - sess: WSF_ROUTER_SESSION - do - create sess - router.dispatch (req, res, sess) - if not sess.dispatched then - execute_default (req, res) - end - end - - execute_default (req: WSF_REQUEST; res: WSF_RESPONSE) - -- Dispatch requests without a matching handler. - local - msg: WSF_DEFAULT_ROUTER_RESPONSE - do - create msg.make_with_router (request, router) - msg.set_documentation_included (True) - response.send (msg) + process_execution (request, response) end note diff --git a/library/server/wsf/router/wsf_routed_executor.e b/library/server/wsf/router/wsf_routed_executor.e new file mode 100644 index 00000000..254be180 --- /dev/null +++ b/library/server/wsf/router/wsf_routed_executor.e @@ -0,0 +1,91 @@ +note + description: "Summary description for {WSF_ROUTED_EXECUTOR}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + WSF_ROUTED_EXECUTOR + +inherit + WSF_EXECUTOR + redefine + initialize + end + + WSF_ROUTED + +feature {NONE} -- Initialize + + initialize + do + Precursor + initialize_router + end + +feature -- Router + + initialize_router + -- Initialize router + do + create_router + setup_router + end + + create_router + -- Create `router' + --| could be redefine to initialize with proper capacity + do + create router.make (10) + ensure + router_created: router /= Void + end + + setup_router + -- Setup `router' + require + router_created: router /= Void + deferred + end + +feature -- Access + + router: WSF_ROUTER + -- Router used to dispatch the request according to the WSF_REQUEST object + -- and associated request methods + +feature -- Execution + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + local + sess: WSF_ROUTER_SESSION + do + create sess + router.dispatch (req, res, sess) + if not sess.dispatched then + execute_default (req, res) + end + end + + execute_default (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Dispatch requests without a matching handler. + local + msg: WSF_DEFAULT_ROUTER_RESPONSE + do + create msg.make_with_router (req, router) + msg.set_documentation_included (True) + res.send (msg) + end + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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/src/wsf_execution.e b/library/server/wsf/src/wsf_execution.e index 8bc0a978..c05cca73 100644 --- a/library/server/wsf/src/wsf_execution.e +++ b/library/server/wsf/src/wsf_execution.e @@ -1,7 +1,7 @@ note description: "Request execution based on attributes `request' and `response'. " - date: "$Date$" - revision: "$Revision$" + date: "$Date: 2015-06-16 15:03:22 +0200 (mar., 16 juin 2015) $" + revision: "$Revision: 97475 $" deferred class WSF_EXECUTION @@ -11,7 +11,7 @@ inherit rename request as wgi_request, response as wgi_response, - make_from_execution as make_from_wgi_execution + make_from_execution as make_from_wgi_execution redefine make, execute, diff --git a/library/server/wsf/src/wsf_executor.e b/library/server/wsf/src/wsf_executor.e new file mode 100644 index 00000000..6a12d15e --- /dev/null +++ b/library/server/wsf/src/wsf_executor.e @@ -0,0 +1,31 @@ +note + description: "Request execution with request and response passed as argument." + date: "$Date$" + revision: "$Revision$" + +deferred class + WSF_EXECUTOR + +feature {NONE} -- Initialization + + initialize + do + end + +feature -- Execution + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + deferred + end + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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