From b4a9c92ffcca111db1f1c78be23a02a9a2ebc492 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 25 Mar 2015 14:56:38 +0100 Subject: [PATCH] Migrated simple, simple_file and upload_image example. Adapted EWF accordingly. --- examples/upload_image/src/image_uploader.e | 223 +------------- .../src/image_uploader_execution.e | 261 ++++++++++++++++ .../ewsgi/connectors/nino/src/nino_service.e | 48 ++- .../connectors/nino/src/wgi_nino_connector.e | 20 +- .../connector}/wgi_execution.e | 0 .../connector}/wgi_exporter.e | 0 .../nino/wsf_nino_service_launcher.e | 2 +- .../nino/wsf_default_response_service.e | 8 +- .../service/wsf_routed_skeleton_execution.e | 291 ++++++++++++++++++ .../wsf_uri_helper_for_routed_execution.e | 60 ++++ ...uri_template_helper_for_routed_execution.e | 73 +++++ .../server/wsf/router/wsf_routed_execution.e | 76 +++++ library/server/wsf/src/wsf_execution.e | 8 + 13 files changed, 808 insertions(+), 262 deletions(-) create mode 100644 examples/upload_image/src/image_uploader_execution.e rename library/server/ewsgi/{src => specification/connector}/wgi_execution.e (100%) rename library/server/ewsgi/{src => specification/connector}/wgi_exporter.e (100%) create mode 100644 library/server/wsf/router/policy/service/wsf_routed_skeleton_execution.e create mode 100644 library/server/wsf/router/support/uri/helpers/wsf_uri_helper_for_routed_execution.e create mode 100644 library/server/wsf/router/support/uri_template/helpers/wsf_uri_template_helper_for_routed_execution.e create mode 100644 library/server/wsf/router/wsf_routed_execution.e diff --git a/examples/upload_image/src/image_uploader.e b/examples/upload_image/src/image_uploader.e index 02bbbd69..06f1037d 100644 --- a/examples/upload_image/src/image_uploader.e +++ b/examples/upload_image/src/image_uploader.e @@ -10,16 +10,7 @@ class inherit ANY - WSF_ROUTED_SKELETON_SERVICE - undefine - requires_proxy - end - - WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_SERVICE - - WSF_NO_PROXY_POLICY - - WSF_DEFAULT_SERVICE + WSF_DEFAULT_SERVICE [IMAGE_UPLOADER_EXECUTION] SHARED_EXECUTION_ENVIRONMENT @@ -31,224 +22,12 @@ feature {NONE} -- Initialization make -- Initialize Current do - initialize_router - - -- To use particular port number (as 9090) with Nino connector -- Uncomment the following line set_service_option ("port", 9090) make_and_launch end - setup_router - -- Setup router - local - www: WSF_FILE_SYSTEM_HANDLER - do - map_uri_template_agent_with_request_methods ("/upload/{name}{?nb}", agent execute_upload_put, router.methods_put) - map_uri_template_agent ("/upload{?nb}", agent execute_upload) - - create www.make_with_path (document_root) - www.set_directory_index (<<"index.html">>) - www.set_not_found_handler (agent execute_not_found) - router.handle_with_request_methods ("", www, router.methods_GET) - end - -feature -- Configuration - - document_root: PATH - -- Document root to look for files or directories - once - Result := execution_environment.current_working_path.extended ("htdocs") - ensure - not Result.name.ends_with (Operating_environment.directory_separator.out) - end - - files_root: PATH - -- Uploaded files will be stored in `files_root' folder - once - Result := document_root.extended ("files") - end - -feature -- Execution - - execute_not_found (uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE) - -- `uri' is not found, redirect to default page - do - res.redirect_now_with_content (req.script_url ("/"), uri + ": not found.%NRedirection to " + req.script_url ("/"), "text/html") - end - - execute_upload (req: WSF_REQUEST; res: WSF_RESPONSE) - -- Upload page is requested, either GET or POST - -- On GET display the web form to upload file, by passing ?nb=5 you can upload 5 images - -- On POST display the uploaded files - local - l_body: STRING_8 - l_safe_filename: STRING_8 - fn: PATH - page: WSF_HTML_PAGE_RESPONSE - n: INTEGER - do - if req.is_request_method ("GET") or else not req.has_uploaded_file then - create page.make - page.set_title ("EWF: Upload file") - page.add_style (req.script_url ("style.css"), "all") - create l_body.make_empty - page.set_body (l_body) - l_body.append ("

EWF: Upload image file

%N") - l_body.append ("
%N") - if attached {WSF_STRING} req.query_parameter ("nb") as p_nb and then p_nb.is_integer then - n := p_nb.integer_value - else - n := 1 - end - if attached {WSF_STRING} req.query_parameter ("demo") as p_demo then - fn := document_root.extended (p_demo.value) - l_body.append ("File:
%N") - end - - from - until - n = 0 - loop - l_body.append ("File:
%N") - n := n - 1 - end - l_body.append (" %N
") - res.send (page) - else - create l_body.make (255) - l_body.append ("

EWF: Uploaded files

%N") - l_body.append ("") - - create page.make - page.set_title ("EWF: uploaded image") - page.add_style ("../style.css", "all") - page.set_body (l_body) - res.send (page) - end - end - - execute_upload_put (req: WSF_REQUEST; res: WSF_RESPONSE) - -- Upload page is requested, PUT - require - is_put_request_method: req.is_put_request_method - local - l_body: STRING_8 - l_safe_filename: detachable READABLE_STRING_32 - fn: PATH - page: WSF_HTML_PAGE_RESPONSE - n: INTEGER - do - create l_body.make (255) - l_body.append ("

EWF: Uploaded files

%N") - l_body.append ("") - - create page.make - page.set_title ("EWF: uploaded image") - page.add_style ("../style.css", "all") - page.set_body (l_body) - res.send (page) - end - - -feature {NONE} -- Encoder - - new_temporary_output_file (n: detachable READABLE_STRING_8): detachable FILE - local - bp: detachable PATH - d: DIRECTORY - i: INTEGER - do - create bp.make_current - create d.make_with_path (bp) - if not d.exists then - d.recursive_create_dir - end - if n /= Void then - bp := bp.extended ("tmp-download-" + n) - else - bp := bp.extended ("tmp") - end - from - i := 0 - until - Result /= Void or i > 100 - loop - i := i + 1 - create {RAW_FILE} Result.make_with_path (bp.appended ("__" + i.out)) - if Result.exists then - Result := Void - else - Result.open_write - end - end - ensure - Result /= Void implies Result.is_open_write - end - - url_encode (s: READABLE_STRING_32): STRING_8 - -- URL Encode `s' as Result - do - Result := url_encoder.encoded_string (s) - end - - url_encoder: URL_ENCODER - once - create Result - end - - html_encode (s: READABLE_STRING_32): STRING_8 - -- HTML Encode `s' as Result - do - Result := html_encoder.encoded_string (s) - end - - html_encoder: HTML_ENCODER - once - create Result - end - note copyright: "2011-2012, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/examples/upload_image/src/image_uploader_execution.e b/examples/upload_image/src/image_uploader_execution.e new file mode 100644 index 00000000..84f51c87 --- /dev/null +++ b/examples/upload_image/src/image_uploader_execution.e @@ -0,0 +1,261 @@ +note + description: "Summary description for {IMAGE_UPLOADER_EXECUTION}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + IMAGE_UPLOADER_EXECUTION + +inherit + WSF_EXECUTION + redefine + initialize + end + + WSF_ROUTED_SKELETON_EXECUTION + undefine + requires_proxy + end + + WSF_NO_PROXY_POLICY + + WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_EXECUTION + +create + make + +feature {NONE} -- Initialization + + initialize + do + Precursor + initialize_router + end + + setup_router + -- Setup router + local + www: WSF_FILE_SYSTEM_HANDLER + do + map_uri_template_agent_with_request_methods ("/upload/{name}{?nb}", agent execute_upload_put, router.methods_put) + map_uri_template_agent ("/upload{?nb}", agent execute_upload) + + create www.make (document_root) + www.set_directory_index (<<"index.html">>) + www.set_not_found_handler (agent execute_not_found) + router.handle_with_request_methods ("", www, router.methods_GET) + end + +feature -- Configuration + + document_root: READABLE_STRING_8 + -- Document root to look for files or directories + local + e: EXECUTION_ENVIRONMENT + dn: DIRECTORY_NAME + once + create e + create dn.make_from_string (e.current_working_directory) + dn.extend ("htdocs") + Result := dn.string + if Result [Result.count] = Operating_environment.directory_separator then + Result := Result.substring (1, Result.count - 1) + end + ensure + not Result.ends_with (Operating_environment.directory_separator.out) + end + + files_root: READABLE_STRING_8 + -- Uploaded files will be stored in `files_root' folder + local + dn: DIRECTORY_NAME + once + create dn.make_from_string (document_root) + dn.extend ("files") + Result := dn.string + end + +feature -- Execution + + execute_not_found (uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE) + -- `uri' is not found, redirect to default page + do + res.redirect_now_with_content (req.script_url ("/"), uri + ": not found.%NRedirection to " + req.script_url ("/"), "text/html") + end + + execute_upload (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Upload page is requested, either GET or POST + -- On GET display the web form to upload file, by passing ?nb=5 you can upload 5 images + -- On POST display the uploaded files + local + l_body: STRING_8 + l_safe_filename: STRING_8 + fn: PATH + page: WSF_HTML_PAGE_RESPONSE + n: INTEGER + do + if req.is_request_method ("GET") or else not req.has_uploaded_file then + create page.make + page.set_title ("EWF: Upload file") + page.add_style (req.script_url ("style.css"), "all") + create l_body.make_empty + page.set_body (l_body) + l_body.append ("

EWF: Upload image file

%N") + l_body.append ("
%N") + if attached {WSF_STRING} req.query_parameter ("nb") as p_nb and then p_nb.is_integer then + n := p_nb.integer_value + else + n := 1 + end + if attached {WSF_STRING} req.query_parameter ("demo") as p_demo then + create fn.make_from_string (document_root) + fn := fn.extended (p_demo.value) + l_body.append ("File:
%N") + end + + from + until + n = 0 + loop + l_body.append ("File:
%N") + n := n - 1 + end + l_body.append (" %N
") + res.send (page) + else + create l_body.make (255) + l_body.append ("

EWF: Uploaded files

%N") + l_body.append ("") + + create page.make + page.set_title ("EWF: uploaded image") + page.add_style ("../style.css", "all") + page.set_body (l_body) + res.send (page) + end + end + + execute_upload_put (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Upload page is requested, PUT + require + is_put_request_method: req.is_put_request_method + local + l_body: STRING_8 + l_safe_filename: detachable READABLE_STRING_32 + fn: PATH + page: WSF_HTML_PAGE_RESPONSE + n: INTEGER + do + create l_body.make (255) + l_body.append ("

EWF: Uploaded files

%N") + l_body.append ("") + + create page.make + page.set_title ("EWF: uploaded image") + page.add_style ("../style.css", "all") + page.set_body (l_body) + res.send (page) + end + + +feature {NONE} -- Encoder + + new_temporary_output_file (n: detachable READABLE_STRING_8): detachable FILE + local + bp: detachable PATH + d: DIRECTORY + i: INTEGER + do + create bp.make_current + create d.make_with_path (bp) + if not d.exists then + d.recursive_create_dir + end + if n /= Void then + bp := bp.extended ("tmp-download-" + n) + else + bp := bp.extended ("tmp") + end + from + i := 0 + until + Result /= Void or i > 100 + loop + i := i + 1 + create {RAW_FILE} Result.make_with_path (bp.appended ("__" + i.out)) + if Result.exists then + Result := Void + else + Result.open_write + end + end + ensure + Result /= Void implies Result.is_open_write + end + + url_encode (s: READABLE_STRING_32): STRING_8 + -- URL Encode `s' as Result + do + Result := url_encoder.encoded_string (s) + end + + url_encoder: URL_ENCODER + once + create Result + end + + html_encode (s: READABLE_STRING_32): STRING_8 + -- HTML Encode `s' as Result + do + Result := html_encoder.encoded_string (s) + end + + html_encoder: HTML_ENCODER + once + create Result + end + +end diff --git a/library/server/ewsgi/connectors/nino/src/nino_service.e b/library/server/ewsgi/connectors/nino/src/nino_service.e index fd020b39..8f596a28 100644 --- a/library/server/ewsgi/connectors/nino/src/nino_service.e +++ b/library/server/ewsgi/connectors/nino/src/nino_service.e @@ -4,50 +4,48 @@ note revision: "$Revision$" class - NINO_SERVICE + NINO_SERVICE [G -> WGI_EXECUTION create make end] create make, - make_custom, - make_with_callback, - make_custom_with_callback + make_custom feature {NONE} -- Implementation - make (a_service: WGI_SERVICE) + make -- Initialize `Current'. do - make_custom (a_service, Void) + make_custom (Void) end - make_custom (a_service: WGI_SERVICE; a_base_url: detachable STRING) + make_custom (a_base_url: detachable STRING) -- Initialize `Current'. require base_url_starts_with_slash: (a_base_url /= Void and then not a_base_url.is_empty) implies a_base_url.starts_with ("/") do - create connector.make_with_base (a_service, a_base_url) + create connector.make_with_base (a_base_url) end - make_with_callback (a_callback: PROCEDURE [ANY, TUPLE [req: WGI_REQUEST; res: WGI_RESPONSE]]) - -- Initialize `Current'. - do - make_custom_with_callback (a_callback, Void) - end +-- make_with_callback (a_callback: PROCEDURE [ANY, TUPLE [req: WGI_REQUEST; res: WGI_RESPONSE]]) +-- -- Initialize `Current'. +-- do +-- make_custom_with_callback (a_callback, Void) +-- end - make_custom_with_callback (a_callback: PROCEDURE [ANY, TUPLE [req: WGI_REQUEST; res: WGI_RESPONSE]]; a_base_url: detachable STRING) - -- Initialize `Current'. - require - base_url_starts_with_slash: (a_base_url /= Void and then not a_base_url.is_empty) implies a_base_url.starts_with ("/") - local - app: WGI_AGENT_SERVICE - do - create app.make (a_callback) - make_custom (app, a_base_url) - end +-- make_custom_with_callback (a_callback: PROCEDURE [ANY, TUPLE [req: WGI_REQUEST; res: WGI_RESPONSE]]; a_base_url: detachable STRING) +-- -- Initialize `Current'. +-- require +-- base_url_starts_with_slash: (a_base_url /= Void and then not a_base_url.is_empty) implies a_base_url.starts_with ("/") +-- local +-- app: WGI_AGENT_SERVICE +-- do +-- create app.make (a_callback) +-- make_custom (app, a_base_url) +-- end feature -- Access - connector: WGI_NINO_CONNECTOR + connector: WGI_NINO_CONNECTOR [G] -- Web server connector feature -- Status report @@ -104,7 +102,7 @@ feature -- Server end note - copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/server/ewsgi/connectors/nino/src/wgi_nino_connector.e b/library/server/ewsgi/connectors/nino/src/wgi_nino_connector.e index 03696fd6..7543a0df 100644 --- a/library/server/ewsgi/connectors/nino/src/wgi_nino_connector.e +++ b/library/server/ewsgi/connectors/nino/src/wgi_nino_connector.e @@ -15,11 +15,11 @@ create feature {NONE} -- Initialization - make (a_service: like service) + make --(a_service: like service) local cfg: HTTP_SERVER_CONFIGURATION do - service := a_service +-- service := a_service create cfg.make create server.make (cfg) @@ -29,11 +29,11 @@ feature {NONE} -- Initialization create on_stopped_actions end - make_with_base (a_service: like service; a_base: like base) + make_with_base (a_base: like base) require a_base_starts_with_slash: (a_base /= Void and then not a_base.is_empty) implies a_base.starts_with ("/") do - make (a_service) + make -- (a_service) set_base (a_base) end @@ -45,10 +45,10 @@ feature -- Access version: STRING_8 = "0.1" -- Version of Current connector -feature {NONE} -- Access +--feature {NONE} -- Access - service: WGI_SERVICE - -- Gateway Service +-- service: WGI_SERVICE +-- -- Gateway Service feature -- Access @@ -139,7 +139,7 @@ feature -- Server create req.make (env, create {WGI_NINO_INPUT_STREAM}.make (a_socket), Current) create res.make (create {WGI_NINO_OUTPUT_STREAM}.make (a_socket), create {WGI_NINO_ERROR_STREAM}.make_stderr (a_socket.descriptor.out)) req.set_meta_string_variable ("RAW_HEADER_DATA", a_headers_text) - + create {G} exec.make (req, res) exec.execute res.flush @@ -161,7 +161,7 @@ feature -- Server end if exec /= Void then exec.clean - end + end end rescue if not retried then @@ -171,7 +171,7 @@ feature -- Server end note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/server/ewsgi/src/wgi_execution.e b/library/server/ewsgi/specification/connector/wgi_execution.e similarity index 100% rename from library/server/ewsgi/src/wgi_execution.e rename to library/server/ewsgi/specification/connector/wgi_execution.e diff --git a/library/server/ewsgi/src/wgi_exporter.e b/library/server/ewsgi/specification/connector/wgi_exporter.e similarity index 100% rename from library/server/ewsgi/src/wgi_exporter.e rename to library/server/ewsgi/specification/connector/wgi_exporter.e diff --git a/library/server/wsf/connector/nino/wsf_nino_service_launcher.e b/library/server/wsf/connector/nino/wsf_nino_service_launcher.e index 553f20fe..35038b09 100644 --- a/library/server/wsf/connector/nino/wsf_nino_service_launcher.e +++ b/library/server/wsf/connector/nino/wsf_nino_service_launcher.e @@ -67,7 +67,7 @@ feature {NONE} -- Initialization verbose := l_verbose_str.as_lower.same_string ("true") end end - create conn.make (Current) + create conn.make --(Current) connector := conn conn.on_launched_actions.extend (agent on_launched) diff --git a/library/server/wsf/default/nino/wsf_default_response_service.e b/library/server/wsf/default/nino/wsf_default_response_service.e index 0cefc7da..db3fddc6 100644 --- a/library/server/wsf/default/nino/wsf_default_response_service.e +++ b/library/server/wsf/default/nino/wsf_default_response_service.e @@ -4,15 +4,15 @@ note revision: "$Revision$" deferred class - WSF_DEFAULT_RESPONSE_SERVICE + WSF_DEFAULT_RESPONSE_SERVICE [G -> WSF_EXECUTION create make end] inherit - WSF_DEFAULT_SERVICE + WSF_DEFAULT_SERVICE [G] - WSF_RESPONSE_SERVICE + WSF_RESPONSE_SERVICE [G] note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/server/wsf/router/policy/service/wsf_routed_skeleton_execution.e b/library/server/wsf/router/policy/service/wsf_routed_skeleton_execution.e new file mode 100644 index 00000000..7daacd25 --- /dev/null +++ b/library/server/wsf/router/policy/service/wsf_routed_skeleton_execution.e @@ -0,0 +1,291 @@ +note + description: "Summary description for {WSF_ROUTED_SKELETON_EXECUTION}." + date: "$Date$" + revision: "$Revision$" + +deferred class + WSF_ROUTED_SKELETON_EXECUTION + +inherit + WSF_ROUTED_EXECUTION + redefine + execute + end + + WSF_SYSTEM_OPTIONS_ACCESS_POLICY + + WSF_PROXY_USE_POLICY + +feature -- Execution + + execute + -- 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'. + local + l_sess: WSF_ROUTER_SESSION + req: WSF_REQUEST; res: WSF_RESPONSE + do + req := request + res := response + + --| 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 + create l_sess + router.dispatch (req, res, l_sess) + if not l_sess.dispatched then + execute_default + end + 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-2013, 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/support/uri/helpers/wsf_uri_helper_for_routed_execution.e b/library/server/wsf/router/support/uri/helpers/wsf_uri_helper_for_routed_execution.e new file mode 100644 index 00000000..ee087582 --- /dev/null +++ b/library/server/wsf/router/support/uri/helpers/wsf_uri_helper_for_routed_execution.e @@ -0,0 +1,60 @@ +note + description: "Facilities inheritance to add URI base routing to a routed execution" + + date: "$Date$" + revision: "$Revision$" + +deferred class WSF_URI_HELPER_FOR_ROUTED_EXECUTION + +feature -- Access + + router: WSF_ROUTER + -- Router used to dispatch the request according to the WSF_REQUEST object + -- and associated request methods; + -- This should not be implemented by descendants. Instead, you gain an effective + -- version by also inheriting from WSF_ROUTED_EXECUTION, or one of it's descendants. + deferred + ensure + router_not_void: Result /= Void + end + +feature -- Mapping helper: uri + + map_uri (a_uri: READABLE_STRING_8; h: WSF_URI_HANDLER) + -- Map `h' as handler for `a_uri' + do + map_uri_with_request_methods (a_uri, h, Void) + end + + map_uri_with_request_methods (a_uri: READABLE_STRING_8; h: WSF_URI_HANDLER; rqst_methods: detachable WSF_REQUEST_METHODS) + -- Map `h' as handler for `a_uri' for request methods `rqst_methods'. + do + router.map_with_request_methods (create {WSF_URI_MAPPING}.make (a_uri, h), rqst_methods) + end + +feature -- Mapping helper: uri agent + + map_uri_agent (a_uri: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]) + -- Map `proc' as handler for `a_uri' + do + map_uri_agent_with_request_methods (a_uri, proc, Void) + end + + map_uri_agent_with_request_methods (a_uri: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]; rqst_methods: detachable WSF_REQUEST_METHODS) + -- Map `proc' as handler for `a_uri' for request methods `rqst_methods'. + do + map_uri_with_request_methods (a_uri, create {WSF_URI_AGENT_HANDLER}.make (proc), rqst_methods) + end + +note + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, 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/support/uri_template/helpers/wsf_uri_template_helper_for_routed_execution.e b/library/server/wsf/router/support/uri_template/helpers/wsf_uri_template_helper_for_routed_execution.e new file mode 100644 index 00000000..223b8292 --- /dev/null +++ b/library/server/wsf/router/support/uri_template/helpers/wsf_uri_template_helper_for_routed_execution.e @@ -0,0 +1,73 @@ +note + description: "Facilities inheritance to add URI template-base routing to a routed execution" + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_EXECUTION + +feature -- Access + + router: WSF_ROUTER + -- Router used to dispatch the request according to the WSF_REQUEST object + -- and associated request methods; + -- This should not be implemented by descendants. Instead, you gain an effective + -- version by also inheriting from WSF_ROUTED_SERVICE, or one of it's descendants. + deferred + ensure + router_not_void: Result /= Void + end + +feature -- Mapping helper: uri template + + map_uri_template (a_tpl: STRING; h: WSF_URI_TEMPLATE_HANDLER) + -- Map `h' as handler for `a_tpl' + require + a_tpl_attached: a_tpl /= Void + h_attached: h /= Void + do + map_uri_template_with_request_methods (a_tpl, h, Void) + end + + map_uri_template_with_request_methods (a_tpl: READABLE_STRING_8; h: WSF_URI_TEMPLATE_HANDLER; rqst_methods: detachable WSF_REQUEST_METHODS) + -- Map `h' as handler for `a_tpl' for request methods `rqst_methods'. + require + a_tpl_attached: a_tpl /= Void + h_attached: h /= Void + do + router.map_with_request_methods (create {WSF_URI_TEMPLATE_MAPPING}.make (a_tpl, h), rqst_methods) + end + +feature -- Mapping helper: uri template agent + + map_uri_template_agent (a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]) + -- Map `proc' as handler for `a_tpl' + require + a_tpl_attached: a_tpl /= Void + proc_attached: proc /= Void + do + map_uri_template_agent_with_request_methods (a_tpl, proc, Void) + end + + map_uri_template_agent_with_request_methods (a_tpl: READABLE_STRING_8; proc: PROCEDURE [ANY, TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]; rqst_methods: detachable WSF_REQUEST_METHODS) + -- Map `proc' as handler for `a_tpl' for request methods `rqst_methods'. + require + a_tpl_attached: a_tpl /= Void + proc_attached: proc /= Void + do + map_uri_template_with_request_methods (a_tpl, create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (proc), rqst_methods) + end + +note + copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, 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 new file mode 100644 index 00000000..40581dcc --- /dev/null +++ b/library/server/wsf/router/wsf_routed_execution.e @@ -0,0 +1,76 @@ +note + description: "Summary description for {WSF_ROUTED_EXECUTION}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + WSF_ROUTED_EXECUTION + +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 + + request: WSF_REQUEST + deferred + end + + response: WSF_RESPONSE + deferred + end + + 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'. + local + sess: WSF_ROUTER_SESSION + do + create sess + router.dispatch (request, response, sess) + if not sess.dispatched then + execute_default + end + ensure + response_status_is_set: response.status_is_set + end + + execute_default + -- 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) + end + +end diff --git a/library/server/wsf/src/wsf_execution.e b/library/server/wsf/src/wsf_execution.e index b99b89c0..9af7db08 100644 --- a/library/server/wsf/src/wsf_execution.e +++ b/library/server/wsf/src/wsf_execution.e @@ -28,6 +28,14 @@ feature {NONE} -- Initialization Precursor (req, res) create request.make_from_wgi (wgi_request) create response.make_from_wgi (wgi_response) + initialize + end + + initialize + -- Initialize Current object. + --| To be redefined if needed. + do + end feature {NONE} -- Access