Introduced WSF_ROUTER_SESSION

This fixes CQS violation from WSF_ROUTER.dispatch_and_return_handler (...): ? WSF_HANDLER
and related code, and this is more compliant with concurrency.

In addition, the WSF_ROUTER_SESSION can be enhanced in the future to answer more advanced needs.
This commit is contained in:
Jocelyn Fiat
2013-03-21 15:41:46 +01:00
parent ade9a30c03
commit 7c7bf9a3f8
9 changed files with 118 additions and 54 deletions

View File

@@ -44,10 +44,12 @@ feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE) execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter -- Execute the filter
local
sess: WSF_ROUTER_SESSION
do do
if attached router.dispatch_and_return_handler (req, res) then create sess
check router.is_dispatched end router.dispatch (req, res, sess)
else if not sess.dispatched then
execute_default (req, res) execute_default (req, res)
end end
execute_next (req, res) execute_next (req, res)
@@ -63,7 +65,7 @@ feature -- Basic operations
end end
note note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" 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)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -58,7 +58,7 @@ feature -- Status
Result := p.starts_with (s) Result := p.starts_with (s)
end end
routed_handler (req: WSF_REQUEST; res: WSF_RESPONSE; a_router: WSF_ROUTER): detachable WSF_HANDLER try (req: WSF_REQUEST; res: WSF_RESPONSE; sess: WSF_ROUTER_SESSION; a_router: WSF_ROUTER)
-- Return the handler if Current matches the request `req'. -- Return the handler if Current matches the request `req'.
local local
p: READABLE_STRING_8 p: READABLE_STRING_8
@@ -67,7 +67,7 @@ feature -- Status
p := path_from_request (req) p := path_from_request (req)
s := based_uri (uri, a_router) s := based_uri (uri, a_router)
if p.starts_with (s) then if p.starts_with (s) then
Result := handler sess.set_dispatched_handler (handler)
a_router.execute_before (Current) a_router.execute_before (Current)
execute_handler (handler, s, req, res) execute_handler (handler, s, req, res)
a_router.execute_after (Current) a_router.execute_after (Current)
@@ -113,7 +113,7 @@ invariant
uri_attached: uri /= Void uri_attached: uri /= Void
note note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" 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)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -72,10 +72,10 @@ feature -- Status
end end
end end
routed_handler (req: WSF_REQUEST; res: WSF_RESPONSE; a_router: WSF_ROUTER): detachable WSF_HANDLER try (req: WSF_REQUEST; res: WSF_RESPONSE; sess: WSF_ROUTER_SESSION; a_router: WSF_ROUTER)
do do
if is_mapping (req, a_router) then if is_mapping (req, a_router) then
Result := handler sess.set_dispatched_handler (handler)
a_router.execute_before (Current) a_router.execute_before (Current)
execute_handler (handler, req, res) execute_handler (handler, req, res)
a_router.execute_after (Current) a_router.execute_after (Current)
@@ -105,7 +105,7 @@ feature {NONE} -- Implementation
end end
note note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" 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)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -68,7 +68,7 @@ feature -- Status
Result := tpl.match (p) /= Void Result := tpl.match (p) /= Void
end end
routed_handler (req: WSF_REQUEST; res: WSF_RESPONSE; a_router: WSF_ROUTER): detachable WSF_HANDLER try (req: WSF_REQUEST; res: WSF_RESPONSE; sess: WSF_ROUTER_SESSION; a_router: WSF_ROUTER)
-- <Precursor> -- <Precursor>
local local
tpl: URI_TEMPLATE tpl: URI_TEMPLATE
@@ -78,7 +78,7 @@ feature -- Status
p := path_from_request (req) p := path_from_request (req)
tpl := based_uri_template (template, a_router) tpl := based_uri_template (template, a_router)
if attached tpl.match (p) as tpl_res then if attached tpl.match (p) as tpl_res then
Result := handler sess.set_dispatched_handler (handler)
a_router.execute_before (Current) a_router.execute_before (Current)
--| Applied the context to the request --| Applied the context to the request
--| in practice, this will fill the {WSF_REQUEST}.path_parameters --| in practice, this will fill the {WSF_REQUEST}.path_parameters
@@ -126,7 +126,7 @@ feature {NONE} -- Implementation
end end
note note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" 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)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -40,10 +40,12 @@ feature -- Execution
require require
req_attached: req /= Void req_attached: req /= Void
res_attached: res /= Void res_attached: res /= Void
local
sess: WSF_ROUTER_SESSION
do do
if attached router.dispatch_and_return_handler (req, res) as p then create sess
-- executed router.dispatch (req, res, sess)
else if not sess.dispatched then
execute_default (req, res) execute_default (req, res)
end end
end end

View File

@@ -112,75 +112,84 @@ feature -- Mapping handler
map_with_request_methods (f.new_mapping (a_resource), rqst_methods) map_with_request_methods (f.new_mapping (a_resource), rqst_methods)
end end
feature -- Access
is_dispatched: BOOLEAN
-- `dispatch' set `is_dispatched' to True
-- if mapping was found, and associated handler executed
feature -- Basic operations feature -- Basic operations
dispatch (req: WSF_REQUEST; res: WSF_RESPONSE) dispatch (req: WSF_REQUEST; res: WSF_RESPONSE; sess: detachable WSF_ROUTER_SESSION)
-- Dispatch request `req' among the `mappings'. -- Dispatch request `req' among the `mappings'.
-- Set `is_dispatched' if the request were dispatched. -- Set `sess' if the request were dispatched and `sess' attached.
require require
req_attached: req /= Void req_attached: req /= Void
res_attached: res /= Void res_attached: res /= Void
local
l_sess: detachable WSF_ROUTER_SESSION
do do
if attached dispatch_and_return_handler (req, res) then l_sess := sess
check is_dispatched: is_dispatched end if l_sess = Void then
create l_sess
end end
router_dispatch (req, res, l_sess)
end end
dispatch_and_return_handler (req: WSF_REQUEST; res: WSF_RESPONSE): detachable WSF_HANDLER dispatch_and_return_handler (req: WSF_REQUEST; res: WSF_RESPONSE): detachable WSF_HANDLER
-- Dispatch request `req' among the `mappings' -- Dispatch request `req' among the `mappings'
-- And return the associated handler if mapping found and handler executed. -- And return the associated handler if mapping found and handler executed.
--| Violates CQS --| Violates CQS
obsolete
"Use `dispatch' [2013-mar-21]"
require require
req_attached: req /= Void req_attached: req /= Void
res_attached: res /= Void res_attached: res /= Void
local
sess: WSF_ROUTER_SESSION
do
create sess
router_dispatch (req, res, sess)
Result := sess.dispatched_handler
end
feature {NONE} -- Dispatch implementation
router_dispatch (req: WSF_REQUEST; res: WSF_RESPONSE; sess: WSF_ROUTER_SESSION)
require
req_attached: req /= Void
res_attached: res /= Void
sess_attached: sess /= Void
sess_not_dispatched: not sess.dispatched
local local
l_req_method: READABLE_STRING_8 l_req_method: READABLE_STRING_8
head_res: WSF_HEAD_RESPONSE_WRAPPER head_res: WSF_HEAD_RESPONSE_WRAPPER
do do
l_req_method := request_method (req) l_req_method := request_method (req)
is_dispatched := False router_dispatch_for_request_method (req, res, sess, l_req_method)
Result := dispatch_and_return_handler_for_request_method (req, res, l_req_method) if not sess.dispatched and l_req_method = {HTTP_REQUEST_METHODS}.method_head then
if Result = Void and l_req_method = {HTTP_REQUEST_METHODS}.method_head then
check is_not_dispatched: not is_dispatched end
create head_res.make_from_response (res) create head_res.make_from_response (res)
req.set_request_method ({HTTP_REQUEST_METHODS}.method_GET) req.set_request_method ({HTTP_REQUEST_METHODS}.method_GET)
Result := dispatch_and_return_handler_for_request_method (req, head_res, {HTTP_REQUEST_METHODS}.method_GET) router_dispatch_for_request_method (req, head_res, sess, {HTTP_REQUEST_METHODS}.method_GET)
end end
end end
feature {NONE} -- Dispatch implementation router_dispatch_for_request_method (req: WSF_REQUEST; res: WSF_RESPONSE; sess: WSF_ROUTER_SESSION; a_request_method: READABLE_STRING_8)
dispatch_and_return_handler_for_request_method (req: WSF_REQUEST; res: WSF_RESPONSE; a_request_method: READABLE_STRING_8): detachable WSF_HANDLER
-- Dispatch request `req' among the `mappings' -- Dispatch request `req' among the `mappings'
-- And return the associated handler if mapping found and handler executed. -- And return the associated handler if mapping found and handler executed.
--| Violates CQS --| Violates CQS
require require
req_attached: req /= Void req_attached: req /= Void
res_attached: res /= Void res_attached: res /= Void
sess_attached: sess /= Void
sess_not_dispatched: not sess.dispatched
a_request_method_attached: a_request_method /= Void a_request_method_attached: a_request_method /= Void
local local
m: WSF_ROUTER_MAPPING m: WSF_ROUTER_MAPPING
do do
is_dispatched := False
across across
mappings as c mappings as c
until until
Result /= Void sess.dispatched
loop loop
if attached c.item as l_info then if attached c.item as l_info then
if is_matching_request_methods (a_request_method, l_info.request_methods) then if is_matching_request_methods (a_request_method, l_info.request_methods) then
m := l_info.mapping m := l_info.mapping
if attached m.routed_handler (req, res, Current) as r then m.try (req, res, sess, Current)
is_dispatched := True
Result := r
end
end end
end end
end end

View File

@@ -1,6 +1,7 @@
note note
description: "Summary description for {WSF_ROUTER_MAPPING}." description: "[
author: "" Describes a route or mapping for the WSF_ROUTER
]"
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
@@ -23,14 +24,14 @@ feature {NONE} -- Initialization
feature -- Access feature -- Access
associated_resource: READABLE_STRING_8 associated_resource: READABLE_STRING_8
-- Name (URI, or URI template or regular expression or ...) of handled resource -- Name (URI, or URI template or regular expression or ...) of handled resource.
deferred deferred
ensure ensure
assciated_resource_not_void: Result /= Void assciated_resource_not_void: Result /= Void
end end
handler: WSF_HANDLER handler: WSF_HANDLER
-- Handler associated with `Current' mapping -- Handler associated with `Current' mapping.
deferred deferred
ensure ensure
handler_attached: Result /= Void handler_attached: Result /= Void
@@ -39,7 +40,7 @@ feature -- Access
feature -- Documentation feature -- Documentation
description: READABLE_STRING_32 description: READABLE_STRING_32
-- Short description of associated mapping -- Short description of associated mapping.
deferred deferred
ensure ensure
description_attached: Result /= Void description_attached: Result /= Void
@@ -63,11 +64,13 @@ feature -- Status
deferred deferred
end end
routed_handler (req: WSF_REQUEST; res: WSF_RESPONSE; a_router: WSF_ROUTER): detachable WSF_HANDLER try (req: WSF_REQUEST; res: WSF_RESPONSE; sess: WSF_ROUTER_SESSION; a_router: WSF_ROUTER)
-- Handler when `Current' matches the request `req' -- Try using `Current' mapping and if it matches request `req'
-- execute associated handler and set this handler in session `sess'.
require require
req_attached: req /= Void req_attached: req /= Void
res_attached: res /= Void res_attached: res /= Void
sess_attached: sess /= Void
a_router_attached: a_router /= Void a_router_attached: a_router /= Void
deferred deferred
end end
@@ -75,7 +78,7 @@ feature -- Status
feature -- Helper feature -- Helper
path_from_request (req: WSF_REQUEST): READABLE_STRING_32 path_from_request (req: WSF_REQUEST): READABLE_STRING_32
-- Path used by `Current' to check that mapping matches request `req' -- Path used by `Current' to check that mapping matches request `req'.
require require
req_attached: req /= Void req_attached: req /= Void
do do

View File

@@ -0,0 +1,46 @@
note
description: "[
This class represents the processing of a request via a WSF_ROUTER.
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_ROUTER_SESSION
feature -- Status report
dispatched: BOOLEAN
-- Handler dispatched?
do
Result := dispatched_handler /= Void
ensure
Result implies dispatched_handler /= Void
end
feature -- Access
dispatched_handler: detachable WSF_HANDLER
-- Handler dispatched
feature -- Change
set_dispatched_handler (h: like dispatched_handler)
do
dispatched_handler := h
ensure
h_set: dispatched_handler = h
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

View File

@@ -54,16 +54,18 @@ feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE) execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler -- Execute request handler
local
sess: WSF_ROUTER_SESSION
do do
if attached router.dispatch_and_return_handler (req, res) as h then create sess
check is_dispatched: router.is_dispatched end router.dispatch (req, res, sess)
else if not sess.dispatched then
res.put_header ({HTTP_STATUS_CODE}.not_found, <<[{HTTP_HEADER_NAMES}.header_content_length, "0"]>>) res.put_header ({HTTP_STATUS_CODE}.not_found, <<[{HTTP_HEADER_NAMES}.header_content_length, "0"]>>)
end end
end end
note note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" 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)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software