From 98ad77a57d10d897ebe52bab59a9170bbf0aab52 Mon Sep 17 00:00:00 2001 From: Colin Adams Date: Sat, 13 Apr 2013 14:49:03 +0100 Subject: [PATCH] If-Match implemented in skeleton handler --- library/server/wsf/router/wsf_get_helper.e | 15 +++ library/server/wsf/router/wsf_method_helper.e | 80 +++++++++++++++ .../wsf/router/wsf_method_helper_factory.e | 25 +++++ .../server/wsf/router/wsf_options_policy.e | 37 +++++++ .../server/wsf/router/wsf_previous_policy.e | 40 ++++++++ .../server/wsf/router/wsf_skeleton_handler.e | 98 +++++++++++++++++++ 6 files changed, 295 insertions(+) create mode 100644 library/server/wsf/router/wsf_get_helper.e create mode 100644 library/server/wsf/router/wsf_method_helper.e create mode 100644 library/server/wsf/router/wsf_method_helper_factory.e create mode 100644 library/server/wsf/router/wsf_options_policy.e create mode 100644 library/server/wsf/router/wsf_previous_policy.e create mode 100644 library/server/wsf/router/wsf_skeleton_handler.e diff --git a/library/server/wsf/router/wsf_get_helper.e b/library/server/wsf/router/wsf_get_helper.e new file mode 100644 index 00000000..e20dd50b --- /dev/null +++ b/library/server/wsf/router/wsf_get_helper.e @@ -0,0 +1,15 @@ +note + + description: "[ + Policy-driven helpers to implement processing of GET and HEAD requests. + ]" + date: "$Date$" + revision: "$Revision$" + +class WSF_GET_HELPER + +inherit + + WSF_METHOD_HELPER + +end diff --git a/library/server/wsf/router/wsf_method_helper.e b/library/server/wsf/router/wsf_method_helper.e new file mode 100644 index 00000000..135876c5 --- /dev/null +++ b/library/server/wsf/router/wsf_method_helper.e @@ -0,0 +1,80 @@ +note + + description: "[ + Policy-driven helpers to implement a method. + ]" + date: "$Date$" + revision: "$Revision$" + +deferred class WSF_METHOD_HELPER + +feature -- Access + + resource_exists: BOOLEAN + -- Does the requested resource (request URI) exist? + +feature -- Setting + + set_resource_exists + -- Set `resource_exists' to `True'. + do + resource_exists := True + ensure + set: resource_exists + end + +feature -- Basic operations + + execute_new_resource (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER) + -- Write response to non-existing resource requested by `req.' into `res'. + -- Policy routines are available in `a_handler'. + -- This default implementation does not apply for PUT requests. + -- The behaviour for POST requests depends upon a policy. + require + req_attached: req /= Void + res_attached: res /= Void + a_handler_attached: a_handler /= Void + do + if a_handler.resource_previously_existed (req) then + --| TODO - should we be passing the entire request, or should we further + --| simplify the programmer's task by passing `req.path_translated'? + if a_handler.resource_moved_permanently (req) then + -- TODO 301 Moved Permanently + elseif a_handler.resource_moved_temporarily (req) then + -- TODO 302 Found + else + -- TODO 410 Gone + end + else + -- TODO 404 Not Found + end + end + + execute_existing_resource (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER) + -- Write response to existing resource requested by `req.' into `res'. + -- Policy routines are available in `a_handler'. + require + req_attached: req /= Void + res_attached: res /= Void + a_handler_attached: a_handler /= Void + not_if_match_star: attached req.http_if_match as l_if_match implies not l_if_match.same_string ("*") + local + l_etags: LIST [READABLE_STRING_32] + do + if attached req.http_if_match as l_if_match then + l_etags := l_if_match.split (',') + + else + -- TODO: check_if_unmodified_since (req, res, a_handler) + end + end + + handle_precondition_failed (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + require + req_attached: req /= Void + res_attached: res /= Void + do + end + +end diff --git a/library/server/wsf/router/wsf_method_helper_factory.e b/library/server/wsf/router/wsf_method_helper_factory.e new file mode 100644 index 00000000..a309b2ff --- /dev/null +++ b/library/server/wsf/router/wsf_method_helper_factory.e @@ -0,0 +1,25 @@ +note + + description: "[ + Default factory for policy-driven method helpers. + Extension methods can be implemented here. + ]" + date: "$Date$" + revision: "$Revision$" + +class WSF_METHOD_HELPER_FACTORY + +feature -- Factory + + new_method_helper (a_method: READABLE_STRING_8): detachable WSF_METHOD_HELPER + -- New object for processing `a_method' + require + a_method_attached: a_method /= Void + do + if a_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_get) or + a_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_head) then + create {WSF_GET_HELPER} Result + end + end + +end diff --git a/library/server/wsf/router/wsf_options_policy.e b/library/server/wsf/router/wsf_options_policy.e new file mode 100644 index 00000000..418ea12b --- /dev/null +++ b/library/server/wsf/router/wsf_options_policy.e @@ -0,0 +1,37 @@ +note + + description: "[ + Default policy for responing to OPTIONS requests other than OPTIONS* + By overriding `execute_options', clients can add a body, for example. + ]" + date: "$Date$" + revision: "$Revision$" + +class WSF_OPTIONS_POLICY + +feature -- Basic operations + + execute_options (req: WSF_REQUEST; res: WSF_RESPONSE; a_router: WSF_ROUTER) + -- Write response to `req' into `res'. + require + req_attached: req /= Void + options_request: req.is_request_method ({HTTP_REQUEST_METHODS}.method_options) + res_attached: res /= Void + a_router_attached: a_router /= Void + local + l_methods: WSF_REQUEST_METHODS + h: HTTP_HEADER + do + create h.make + res.set_status_code ({HTTP_STATUS_CODE}.ok) + h.put_content_type ({HTTP_MIME_TYPES}.text_plain) + h.put_current_date + h.put_content_length (0) + l_methods := a_router.allowed_methods_for_request (req) + if not l_methods.is_empty then + h.put_allow (l_methods) + end + res.put_header_text (h.string) + end + +end diff --git a/library/server/wsf/router/wsf_previous_policy.e b/library/server/wsf/router/wsf_previous_policy.e new file mode 100644 index 00000000..9de00906 --- /dev/null +++ b/library/server/wsf/router/wsf_previous_policy.e @@ -0,0 +1,40 @@ +note + + description: "[ + Policies for deciding if a resource that currently doesn't exist used to do so. + This default implementation assumes that no resources used to exist. + ]" + date: "$Date$" + revision: "$Revision$" + +class WSF_PREVIOUS_POLICY + +feature -- Access + + resource_previously_existed (req: WSF_REQUEST) : BOOLEAN + -- Did `req.path_translated' exist previously? + require + req_attached: req /= Void + do + -- No. Override if this is not want you want. + end + + resource_moved_permanently (req: WSF_REQUEST) : BOOLEAN + -- Was `req.path_translated' moved permanently? + require + req_attached: req /= Void + previously_existed: resource_previously_existed (req) + do + -- No. Override if this is not want you want. + end + + resource_moved_temporarily (req: WSF_REQUEST) : BOOLEAN + -- Was `req.path_translated' moved temporarily? + require + req_attached: req /= Void + previously_existed: resource_previously_existed (req) + do + -- No. Override if this is not want you want. + end + +end diff --git a/library/server/wsf/router/wsf_skeleton_handler.e b/library/server/wsf/router/wsf_skeleton_handler.e new file mode 100644 index 00000000..a6389ee8 --- /dev/null +++ b/library/server/wsf/router/wsf_skeleton_handler.e @@ -0,0 +1,98 @@ +note + + description: "[ + Policy-driven handlers. + Implementers only need to concentrate on creating content. + ]" + date: "$Date$" + revision: "$Revision$" + +deferred class WSF_SKELETON_HANDLER + +inherit + + WSF_URI_TEMPLATE_ROUTING_HANDLER + redefine + execute + end + + WSF_OPTIONS_POLICY + + WSF_PREVIOUS_POLICY + + WSF_METHOD_HELPER_FACTORY + +feature -- Execution + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- + do + check + known_method: True -- Can't be done until WSF_METHOD_NOT_ALLOWED_RESPONSE + -- is refactored. + -- Then maybe this can become a precondition. But we will still (?) + -- need a check that it isn't CONNECT or TRACE (it MIGHT be HEAD). + end + if req.is_request_method ({HTTP_REQUEST_METHODS}.method_options) then + execute_options (req, res, router) + else + if attached new_method_helper (req.request_method) as l_helper then + execute_method (req, res, l_helper) + else + handle_internal_server_error (res) + end + end + end + + execute_method (req: WSF_REQUEST; res: WSF_RESPONSE; a_helper: WSF_METHOD_HELPER) + -- Write response to `req' into `res', using `a_helper' as a logic helper. + require + req_attached: req /= Void + res_attached: res /= Void + a_helper_attached: a_helper /= Void + do + check_resource_exists (req, a_helper) + if a_helper.resource_exists then + a_helper.execute_existing_resource (req, res, Current) + else + if attached req.http_if_match as l_if_match and then l_if_match.same_string ("*") then + a_helper.handle_precondition_failed (req, res) + else + a_helper.execute_new_resource (req, res, Current) + end + end + end + + check_resource_exists (req: WSF_REQUEST; a_helper: WSF_METHOD_HELPER) + -- Call `a_helper.set_resource_exists' to indicate that `req.path_translated' + -- is the name of an existing resource. + -- Optionally, also call `req.set_server_data', if this is now available as a by-product + -- of the existence check. + require + req_attached: req /= Void + a_helper_attached: a_helper /= Void + deferred + end + + handle_internal_server_error (res: WSF_RESPONSE) + -- Write "Internal Server Error" response to `res'. + require + res_attached: res /= Void + local + h: HTTP_HEADER + m: STRING_8 + do + create h.make + h.put_content_type_text_plain + m := "Server failed to handle request properly" + h.put_content_length (m.count) + h.put_current_date + res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error) + res.put_string (m) + ensure + response_status_is_set: res.status_is_set + status_is_service_unavailable: res.status_code = {HTTP_STATUS_CODE}.internal_server_error + body_sent: res.message_committed and then res.transfered_content_length > 0 + end + +end