New ROUTER design, much simpler, less generic, easier to extend, and now one can mix uri map, uri_template map and so on.

Update the "tutorial" example.
This commit is contained in:
Jocelyn Fiat
2012-09-10 20:45:19 +02:00
parent 0f59535696
commit ace897ea2b
31 changed files with 668 additions and 1359 deletions

View File

@@ -13,7 +13,7 @@ class
HELLO_APPLICATION
inherit
WSF_URI_TEMPLATE_ROUTED_SERVICE
WSF_ROUTED_SERVICE
WSF_DEFAULT_SERVICE
redefine
@@ -27,12 +27,34 @@ feature {NONE} -- Initialization
setup_router
do
router.map_agent ("/hello", agent execute_hello)
-- router.map (create {WSF_URI_MAPPING}.make ("/hello", create {WSF_AGENT_URI_HANDLER}.make (agent execute_hello)))
map_agent_uri ("/hello", agent execute_hello, Void)
router.map_with_request_methods ("/users/{user}/message/{mesgid}", create {USER_MESSAGE_HANDLER}, router.methods_HEAD_GET_POST)
router.map_with_request_methods ("/users/{user}/message/", create {USER_MESSAGE_HANDLER}, router.methods_GET_POST)
-- router.map_with_request_methods (create {WSF_URI_TEMPLATE_MAPPING}.make ("/users/{user}/message/{mesgid}", create {USER_MESSAGE_HANDLER}), router.methods_HEAD_GET_POST)
map_uri_template ("/users/{user}/message/{mesgid}", create {USER_MESSAGE_HANDLER}, router.methods_HEAD_GET_POST)
router.map_agent_response_with_request_methods ("/users/{user}/{?op}", agent response_user, router.methods_GET)
-- router.map_with_request_methods (create {WSF_URI_TEMPLATE_MAPPING}.make ("/users/{user}/message/", create {USER_MESSAGE_HANDLER}), router.methods_GET_POST)
map_uri_template ("/users/{user}/message/", create {USER_MESSAGE_HANDLER}, router.methods_GET_POST)
-- router.map_with_request_methods (create {WSF_URI_TEMPLATE_MAPPING}.make ("/users/{user}/{?op}", create {WSF_AGENT_URI_TEMPLATE_RESPONSE_HANDLER}.make (agent response_user)), router.methods_GET)
map_agent_uri_template_response ("/users/{user}/{?op}", agent response_user, router.methods_GET)
end
feature -- Helper: mapping
map_agent_uri (a_uri: READABLE_STRING_8; a_action: like {WSF_AGENT_URI_HANDLER}.action; rqst_methods: detachable WSF_ROUTER_METHODS)
do
router.map_with_request_methods (create {WSF_URI_MAPPING}.make (a_uri, create {WSF_AGENT_URI_HANDLER}.make (a_action)), rqst_methods)
end
map_uri_template (a_tpl: READABLE_STRING_8; a_handler: WSF_URI_TEMPLATE_HANDLER; rqst_methods: detachable WSF_ROUTER_METHODS)
do
router.map_with_request_methods (create {WSF_URI_TEMPLATE_MAPPING}.make (a_tpl, a_handler), rqst_methods)
end
map_agent_uri_template_response (a_tpl: READABLE_STRING_8; a_action: like {WSF_AGENT_URI_TEMPLATE_RESPONSE_HANDLER}.action; rqst_methods: detachable WSF_ROUTER_METHODS)
do
router.map_with_request_methods (create {WSF_URI_TEMPLATE_MAPPING}.make (a_tpl, create {WSF_AGENT_URI_TEMPLATE_RESPONSE_HANDLER}.make (a_action)), rqst_methods)
end
feature -- Execution
@@ -43,7 +65,7 @@ feature -- Execution
res.redirect_now_with_content (req.script_url ("/hello"), "Redirection to " + req.script_url ("/hello"), "text/html")
end
execute_hello (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
execute_hello (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Computed response message.
local
mesg: WSF_HTML_PAGE_RESPONSE
@@ -107,8 +129,9 @@ feature -- Execution
then
if l_op.is_case_insensitive_equal ("quit") then
create redir.make (req.script_url ("/hello"), 3)
redir.set_title ("Bye " + u.url_encoded_value)
redir.set_body ("Bye " + u.url_encoded_value + ",<br/> see you soon.<p>You will be redirected to " +
create html.make
redir.set_title ("Bye " + html.html_encoded_string (l_username))
redir.set_body ("Bye " + html.html_encoded_string (l_username) + ",<br/> see you soon.<p>You will be redirected to " +
redir.url_location + " in " + redir.delay.out + " second(s) ...</p>"
)
Result := redir

View File

@@ -9,7 +9,7 @@ class
USER_MESSAGE_HANDLER
inherit
WSF_RESPONSE_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
WSF_URI_TEMPLATE_RESPONSE_HANDLER
feature -- Access

View File

@@ -1,7 +1,7 @@
# Router
## Requirements
* [URI Template](../../../protocol/rui_template)
* [URI Template](../../../protocol/uri_template)
## Overview

View File

@@ -1,23 +0,0 @@
note
description: "Summary description for {DEFAULT_ROUTED_SERVICE}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_ROUTED_SERVICE
inherit
WSF_ROUTED_SERVICE_I [WSF_HANDLER [WSF_HANDLER_CONTEXT], WSF_HANDLER_CONTEXT]
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -5,12 +5,11 @@ note
revision: "$Revision$"
deferred class
WSF_ROUTED_SERVICE_I [H -> WSF_HANDLER [C], C -> WSF_HANDLER_CONTEXT]
WSF_ROUTED_SERVICE
feature -- Setup
feature -- Initialization
initialize_router
-- Initialize `router'
do
create_router
setup_router
@@ -18,7 +17,8 @@ feature -- Setup
create_router
-- Create `router'
deferred
do
create router.make (10)
ensure
router_created: router /= Void
end
@@ -30,15 +30,12 @@ feature -- Setup
deferred
end
router: WSF_ROUTER [H, C]
-- Request router
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
do
if attached router.route (req) as r then
router.execute_route (r, req, res)
if attached router.dispatch_and_return_handler (req, res) as p then
-- executed
else
execute_default (req, res)
end
@@ -48,7 +45,11 @@ feature -- Execution
deferred
end
note
feature -- Access
router: WSF_ROUTER
;note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[

View File

@@ -0,0 +1,19 @@
note
description: "Summary description for {WSF_STARTS_WITH_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_STARTS_WITH_HANDLER
inherit
WSF_HANDLER
feature -- Execution
execute (a_start_path: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
deferred
end
end

View File

@@ -0,0 +1,62 @@
note
description: "Summary description for EWF_URI_PATH."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_STARTS_WITH_MAPPING
inherit
WSF_ROUTER_MAPPING
create
make
feature {NONE} -- Initialization
make (a_uri: READABLE_STRING_8; h: like handler)
do
handler := h
uri := a_uri
end
feature -- Access
handler: WSF_STARTS_WITH_HANDLER
uri: READABLE_STRING_8
feature -- Status
routed_handler (req: WSF_REQUEST; res: WSF_RESPONSE; a_router: WSF_ROUTER): detachable WSF_HANDLER
local
p: READABLE_STRING_8
s: like based_uri
do
p := source_uri (req)
s := based_uri (uri, a_router)
if p.starts_with (s) then
Result := handler
a_router.execute_before (Current)
handler.execute (s, req, res)
a_router.execute_after (Current)
end
end
feature {NONE} -- Implementation
based_uri (a_uri: like uri; a_router: WSF_ROUTER): like uri
local
s: STRING_8
do
if attached a_router.base_url as l_base_url then
create s.make_from_string (l_base_url)
s.append_string (a_uri)
Result := s
else
Result := a_uri
end
end
end

View File

@@ -1,50 +0,0 @@
note
description: "[
Default router based on URI map
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_ROUTER
inherit
WSF_URI_ROUTER_I [WSF_HANDLER [WSF_URI_HANDLER_CONTEXT], WSF_URI_HANDLER_CONTEXT]
redefine
map_agent_with_request_methods, map_agent_response_with_request_methods
end
create
make
feature -- Mapping agent
map_agent_with_request_methods (a_id: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: WSF_URI_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE]];
rqst_methods: detachable WSF_ROUTER_METHODS)
local
h: WSF_AGENT_HANDLER [WSF_URI_HANDLER_CONTEXT]
do
create h.make (a_action)
map_with_request_methods (a_id, h, rqst_methods)
end
map_agent_response_with_request_methods (a_id: READABLE_STRING_8; a_action: FUNCTION [ANY, TUPLE [ctx: WSF_URI_HANDLER_CONTEXT; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE];
rqst_methods: detachable WSF_ROUTER_METHODS)
local
h: WSF_AGENT_RESPONSE_HANDLER [WSF_URI_HANDLER_CONTEXT]
do
create h.make (a_action)
map_with_request_methods (a_id, h, rqst_methods)
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -1,35 +0,0 @@
note
description: "[
WSF_URI_ROUTING_HANDLER is a default descendant of WSF_URI_ROUTING_HANDLER_I
for WSF_URI_ROUTER
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_ROUTING_HANDLER
inherit
WSF_URI_ROUTING_HANDLER_I [WSF_HANDLER [WSF_URI_HANDLER_CONTEXT], WSF_URI_HANDLER_CONTEXT]
redefine
router
end
create
make
feature {NONE} -- Routing
router: WSF_URI_ROUTER
;note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -1,33 +1,35 @@
note
description: "[
This class helps to build Routing handler based for WSF_URI_ROUTER
]"
description: "Summary description for {WSF_AGENT_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_ROUTING_HANDLER_I [H -> WSF_HANDLER [C],
C -> WSF_URI_HANDLER_CONTEXT create make end]
WSF_AGENT_URI_HANDLER
inherit
WSF_ROUTING_HANDLER [H, C]
WSF_URI_HANDLER
create
make
feature {NONE} -- Initialization
make (n: INTEGER)
make (a_action: like action)
do
create router.make (n)
action := a_action
end
feature {NONE} -- Routing
action: PROCEDURE [ANY, TUPLE [request: WSF_REQUEST; response: WSF_RESPONSE]]
router: WSF_URI_ROUTER_I [H, C]
feature -- Execution
;note
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
do
action.call ([req, res])
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[

View File

@@ -1,13 +1,14 @@
note
description: "Summary description for WSF_AGENT_RESPONSE_HANDLER."
description: "Summary description for {WSF_AGENT_URI_TEMPLATE_RESPONSE_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_AGENT_RESPONSE_HANDLER [C -> WSF_HANDLER_CONTEXT]
WSF_AGENT_URI_TEMPLATE_RESPONSE_HANDLER
inherit
WSF_RESPONSE_HANDLER [C]
WSF_URI_TEMPLATE_RESPONSE_HANDLER
create
make
@@ -21,11 +22,11 @@ feature -- Initialization
feature -- Access
action: FUNCTION [ANY, TUPLE [ctx: C; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE]
action: FUNCTION [ANY, TUPLE [ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE]
feature -- Execution
response (ctx: C; req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
response (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
do
Result := action.item ([ctx, req])
end

View File

@@ -0,0 +1,19 @@
note
description: "Summary description for {EWF_ROUTER_URI_PATH_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_URI_HANDLER
inherit
WSF_HANDLER
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
deferred
end
end

View File

@@ -1,60 +0,0 @@
note
description: "[
Context for the handler execution
It does not provide additional information compared to {WSF_HANDLER_CONTEXT}
- request: WSF_REQUEST -- Associated request
- path: READABLE_STRING_8 -- Associated path
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_HANDLER_CONTEXT
inherit
WSF_HANDLER_CONTEXT
create
make
feature {NONE} -- Initialization
make (req: WSF_REQUEST; p: like path)
do
request := req
path := p
end
feature -- Request data
apply (req: WSF_REQUEST)
-- <Precursor>
do
end
revert (req: WSF_REQUEST)
-- <Precursor>
do
end
feature -- Item
item (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Variable value for parameter or variable `a_name'
-- See `{WSF_REQUEST}.item(s)'
do
Result := request.item (a_name)
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -0,0 +1,80 @@
note
description: "Summary description for EWF_URI_PATH."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_MAPPING
inherit
WSF_ROUTER_MAPPING
create
make,
make_trailing_slash_ignored
feature {NONE} -- Initialization
make (a_uri: READABLE_STRING_8; h: like handler)
do
handler := h
uri := a_uri
end
make_trailing_slash_ignored (a_uri: READABLE_STRING_8; h: like handler)
do
make (a_uri, h)
trailing_slash_ignored := True
end
feature -- Access
handler: WSF_URI_HANDLER
uri: READABLE_STRING_8
trailing_slash_ignored: BOOLEAN
feature -- Status
routed_handler (req: WSF_REQUEST; res: WSF_RESPONSE; a_router: WSF_ROUTER): detachable WSF_HANDLER
local
p: READABLE_STRING_8
l_uri: like uri
do
p := source_uri (req)
l_uri := based_uri (uri, a_router)
if l_uri.ends_with ("/") then
if not p.ends_with ("/") then
p := p + "/"
end
else
if p.ends_with ("/") then
p := p.substring (1, p.count - 1)
end
end
if p.same_string (l_uri) then
Result := handler
a_router.execute_before (Current)
handler.execute (req, res)
a_router.execute_after (Current)
end
end
feature {NONE} -- Implementation
based_uri (a_uri: like uri; a_router: WSF_ROUTER): like uri
local
s: STRING_8
do
if attached a_router.base_url as l_base_url then
create s.make_from_string (l_base_url)
s.append_string (a_uri)
Result := s
else
Result := a_uri
end
end
end

View File

@@ -0,0 +1,41 @@
note
description: "Summary description for {WSF_URI_RESPONSE_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_URI_RESPONSE_HANDLER
inherit
WSF_URI_HANDLER
feature -- Response
response (req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
require
is_valid_context: is_valid_context (req)
deferred
ensure
Result_attached: Result /= Void
end
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
res.send (response (req))
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -1,251 +0,0 @@
note
description: "[
URL dispatcher/router based on simple URI mapping and request methods if precised
The associated context {WSF_URI_HANDLER_CONTEXT} does not contains any additional information.
The matching check if the same path is mapped, or if a substring of the path is mapped
Examples:
map ("/users/", users_handler)
map_with_request_methods ("/groups/", read_groups_handler, <<"GET">>)
map_with_request_methods ("/groups/", write_groups_handler, <<"POST", "PUT", "DELETE">>)
map_agent_with_request_methods ("/order/", agent do_get_order, <<"GET">>)
map_agent_with_request_methods ("/order/", agent do_post_order, <<"POST">>)
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_ROUTER_I [H -> WSF_HANDLER [C], C -> WSF_URI_HANDLER_CONTEXT create make end]
inherit
WSF_ROUTER [H, C]
create
make,
make_with_base_url
feature -- Initialization
make (n: INTEGER)
do
create handlers.make (n)
handlers.compare_objects
initialize
end
make_with_base_url (n: INTEGER; a_base_url: like base_url)
-- Make router allocated for at least `n' maps,
-- and use `a_base_url' as base_url
do
make (n)
set_base_url (a_base_url)
end
feature {WSF_ROUTED_SERVICE_I} -- Status report
handlers_matching_map (a_resource: READABLE_STRING_8; rqst_methods: detachable WSF_ROUTER_METHODS): detachable LIST [H]
local
l_res: READABLE_STRING_8
do
l_res := based_resource (a_resource)
across
handlers as c
loop
if c.item.resource.same_string (l_res) then
if
rqst_methods = Void or else
across rqst_methods as rq some is_matching_request_methods (rq.item, c.item.request_methods) end
then
if Result = Void then
create {ARRAYED_LIST [H]} Result.make (1)
end
Result.extend (c.item.handler)
end
end
end
end
feature {WSF_ROUTED_SERVICE_I} -- Default: implementation
default_handler_context (req: WSF_REQUEST): C
-- <Precursor>
do
Result := handler_context (Void, req)
end
feature -- Registration
map_with_request_methods (p: READABLE_STRING_8; h: H; rqst_methods: detachable WSF_ROUTER_METHODS)
local
l_uri: READABLE_STRING_8
do
if attached base_url as l_base_url then
l_uri := l_base_url + p
else
l_uri := p
end
handlers.force ([h, l_uri, rqst_methods])
h.on_handler_mapped (l_uri, rqst_methods)
end
feature {NONE} -- Implementation
based_resource (a_resource: READABLE_STRING_8): READABLE_STRING_8
do
if attached base_url as l_base_url then
Result := l_base_url + a_resource
else
Result := a_resource
end
end
feature {WSF_ROUTED_SERVICE_I} -- Handler
matching_route (req: WSF_REQUEST): detachable WSF_ROUTE [H, C]
local
h: detachable H
ctx: detachable C
rq_method: READABLE_STRING_8
do
rq_method := request_method (req)
h := handler_by_path (source_uri (req), rq_method)
if h = Void then
if attached smart_handler_by_path (source_uri (req), rq_method) as info then
h := info.handler
ctx := handler_context (info.path, req)
end
end
if h /= Void then
if h.is_valid_context (req) then
if ctx = Void then
ctx := handler_context (Void, req)
end
create Result.make (h, ctx)
else
Result := Void
end
end
end
feature {NONE} -- Access: Implementation
handler_by_path (a_path: READABLE_STRING_GENERAL; rqst_method: READABLE_STRING_8): detachable H
require
a_path_valid: a_path /= Void
local
l_handlers: like handlers
l_item: like handlers.item
do
l_handlers := handlers
from
l_handlers.start
until
l_handlers.after or Result /= Void
loop
l_item := l_handlers.item
if is_matching_request_methods (rqst_method, l_item.request_methods) and a_path.same_string (l_item.resource) then
Result := l_item.handler
end
l_handlers.forth
end
ensure
a_path_unchanged: a_path.same_string (old a_path)
end
smart_handler_by_path (a_path: READABLE_STRING_8; rqst_method: READABLE_STRING_8): detachable TUPLE [path: READABLE_STRING_8; handler: H]
require
a_path_valid: a_path /= Void
local
p: INTEGER
l_context_path, l_path: READABLE_STRING_8
h: detachable H
do
l_context_path := context_path (a_path)
from
p := l_context_path.count + 1
until
p <= 1 or Result /= Void
loop
l_path := l_context_path.substring (1, p - 1)
h := handler_by_path (l_path, rqst_method)
if h /= Void then
Result := [l_path, h]
else
p := l_context_path.last_index_of ('/', p - 1)
end
variant
p
end
ensure
a_path_unchanged: a_path.same_string (old a_path)
end
feature {NONE} -- Context factory
handler_context (p: detachable STRING; req: WSF_REQUEST): C
local
ctx: C
do
if p /= Void then
create ctx.make (req, p)
else
create ctx.make (req, source_uri (req))
end
Result := ctx
end
feature -- Access
new_cursor: ITERATION_CURSOR [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]]
-- Fresh cursor associated with current structure
do
Result := handlers.new_cursor
end
feature {NONE} -- Implementation
handlers: ARRAYED_LIST [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]]
-- Handlers indexed by the template expression
-- see `templates'
context_path (a_path: READABLE_STRING_8): READABLE_STRING_8
-- Prepared path from context which match requirement
-- i.e: not empty, starting with '/'
local
p: INTEGER
s: STRING_8
do
Result := a_path
if Result.is_empty then
Result := "/"
else
if Result[1] /= '/' then
create s.make_from_string (Result)
s.prepend_character ('/')
Result := s
end
p := Result.index_of ('.', 1)
if p > 0 then
Result := Result.substring (1, p - 1)
end
end
ensure
result_not_empty: not Result.is_empty
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -1,25 +1,18 @@
note
description: "[
Request handler object which is called by a WSF_ROUTER
A response handler should implement the method
response (ctx: C; req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
The class is generic, this way one can use a custom WSF_HANDLER_CONTEXT if needed
]"
description: "Summary description for {WSF_URI_TEMPLATE_RESPONSE_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_RESPONSE_HANDLER [C -> WSF_HANDLER_CONTEXT]
WSF_URI_TEMPLATE_RESPONSE_HANDLER
inherit
WSF_HANDLER [C]
WSF_URI_TEMPLATE_HANDLER
feature -- Response
response (ctx: C; req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
response (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
require
is_valid_context: is_valid_context (req)
deferred
@@ -29,7 +22,7 @@ feature -- Response
feature -- Execution
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
execute (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
res.send (response (ctx, req))

View File

@@ -1,39 +0,0 @@
note
description: "Summary description for {DEFAULT_URI_TEMPLATE_ROUTED_SERVICE}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_URI_TEMPLATE_ROUTED_SERVICE
inherit
WSF_ROUTED_SERVICE_I [WSF_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT], WSF_URI_TEMPLATE_HANDLER_CONTEXT]
redefine
router
end
feature {NONE} -- Initialization
create_router
-- Create router
--| it can be redefine to create with precise count if needed.
do
create router.make (0)
end
feature -- Router
router: WSF_URI_TEMPLATE_ROUTER
;note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -1,52 +0,0 @@
note
description: "[
Default router based on URI Template map
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_TEMPLATE_ROUTER
inherit
WSF_URI_TEMPLATE_ROUTER_I [WSF_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT], WSF_URI_TEMPLATE_HANDLER_CONTEXT]
redefine
map_agent_with_request_methods,
map_agent_response_with_request_methods
end
create
make,
make_with_base_url
feature -- Mapping agent
map_agent_with_request_methods (a_id: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE]];
rqst_methods: detachable WSF_ROUTER_METHODS)
local
h: WSF_AGENT_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
do
create h.make (a_action)
map_with_request_methods (a_id, h, rqst_methods)
end
map_agent_response_with_request_methods (a_id: READABLE_STRING_8; a_action: FUNCTION [ANY, TUPLE [ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE];
rqst_methods: detachable WSF_ROUTER_METHODS)
local
h: WSF_AGENT_RESPONSE_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
do
create h.make (a_action)
map_with_request_methods (a_id, h, rqst_methods)
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -1,35 +0,0 @@
note
description: "[
WSF_URI_TEMPLATE_ROUTING_HANDLER is a default descendant of WSF_URI_ROUTING_HANDLER_I
for WSF_URI_TEMPLATE_ROUTER
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_TEMPLATE_ROUTING_HANDLER
inherit
WSF_URI_TEMPLATE_ROUTING_HANDLER_I [WSF_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT], WSF_URI_TEMPLATE_HANDLER_CONTEXT]
redefine
router
end
create
make
feature {NONE} -- Routing
router: WSF_URI_TEMPLATE_ROUTER
;note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -1,31 +1,30 @@
note
description: "Summary description for WSF_AGENT_HANDLER."
description: "Summary description for {WSF_AGENT_URI_TEMPLATE_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_AGENT_HANDLER [C -> WSF_HANDLER_CONTEXT]
WSF_AGENT_URI_TEMPLATE_HANDLER
inherit
WSF_HANDLER [C]
WSF_URI_TEMPLATE_HANDLER
create
make
feature -- Initialization
feature {NONE} -- Initialization
make (act: like action)
make (a_action: like action)
do
action := act
action := a_action
end
feature -- Access
action: PROCEDURE [ANY, TUPLE [ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE]]
action: PROCEDURE [ANY, TUPLE [context: WSF_URI_TEMPLATE_HANDLER_CONTEXT; request: WSF_REQUEST; response: WSF_RESPONSE]]
feature -- Execution
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
execute (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
do
action.call ([ctx, req, res])
end

View File

@@ -0,0 +1,19 @@
note
description: "Summary description for EWF_URI_TEMPLATE_HANDLER."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_URI_TEMPLATE_HANDLER
inherit
WSF_HANDLER
feature -- Execution
execute (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
deferred
end
end

View File

@@ -0,0 +1,81 @@
note
description: "Summary description for {EWF_ROUTER_URI_TEMPLATE_PATH}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_TEMPLATE_MAPPING
inherit
WSF_ROUTER_MAPPING
create
make,
make_from_string
feature {NONE} -- Initialization
make_from_string (s: READABLE_STRING_8; h: like handler)
do
make (create {URI_TEMPLATE}.make (s), h)
end
make (tpl: URI_TEMPLATE; h: like handler)
do
template := tpl
handler := h
end
feature -- Access
handler: WSF_URI_TEMPLATE_HANDLER
template: URI_TEMPLATE
feature -- Element change
set_handler (h: like handler)
do
handler := h
end
feature -- Status
routed_handler (req: WSF_REQUEST; res: WSF_RESPONSE; a_router: WSF_ROUTER): detachable WSF_HANDLER
local
tpl: URI_TEMPLATE
p: READABLE_STRING_32
ctx: detachable WSF_URI_TEMPLATE_HANDLER_CONTEXT
do
p := source_uri (req)
tpl := based_uri_template (template, a_router)
if attached tpl.match (p) as tpl_res then
Result := handler
create ctx.make (req, tpl, tpl_res, source_uri (req))
a_router.execute_before (Current)
ctx.apply (req)
handler.execute (ctx, req, res)
ctx.revert (req)
a_router.execute_after (Current)
end
rescue
if ctx /= Void then
ctx.revert (req)
end
end
feature {NONE} -- Implementation
based_uri_template (a_tpl: like template; a_router: WSF_ROUTER): like template
do
if attached a_router.base_url as l_base_url then
Result := a_tpl.duplicate
Result.set_template (l_base_url + a_tpl.template)
else
Result := a_tpl
end
end
end

View File

@@ -1,228 +0,0 @@
note
description: "[
URL dispatcher/router based on URI Template mapping and request methods if precised
The associated context {WSF_URI_TEMPLATE_HANDLER_CONTEXT} contains information about the matched map.
Examples:
map ("/users/", users_handler)
map_with_request_methods ("/order/{order-id}", order_handler, <<"GET", "POST">>)
map_agent_with_request_methods ("/order/{order-id}", agent do_get_order, <<"GET">>)
map_agent_with_request_methods ("/order/{order-id}", agent do_post_order, <<"POST">>)
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_TEMPLATE_ROUTER_I [H -> WSF_HANDLER [C], C -> WSF_URI_TEMPLATE_HANDLER_CONTEXT create make end]
inherit
WSF_ROUTER [H, C]
create
make,
make_with_base_url
feature -- Initialization
make (n: INTEGER)
do
create handlers.make (n)
create templates.make (n)
handlers.compare_objects
initialize
end
make_with_base_url (n: INTEGER; a_base_url: like base_url)
-- Make router allocated for at least `n' maps,
-- and use `a_base_url' as base_url
do
make (n)
set_base_url (a_base_url)
end
feature -- Status report
handlers_matching_map (a_resource: READABLE_STRING_8; rqst_methods: detachable WSF_ROUTER_METHODS): detachable LIST [H]
local
l_res: READABLE_STRING_8
do
l_res := based_resource (a_resource)
across
handlers as c
loop
if c.item.resource.same_string (l_res) then
if
rqst_methods = Void or else
across rqst_methods as rq some is_matching_request_methods (rq.item, c.item.request_methods) end
then
if Result = Void then
create {ARRAYED_LIST [H]} Result.make (1)
end
Result.extend (c.item.handler)
end
end
end
end
feature -- Registration
map_with_uri_template (uri: URI_TEMPLATE; h: H)
require
has_not_such_map: not has_map (uri.template, Void, h)
do
map_with_uri_template_and_request_methods (uri, h, Void)
end
map_with_uri_template_and_request_methods (uri: URI_TEMPLATE; h: H; rqst_methods: detachable WSF_ROUTER_METHODS)
require
uri_is_valid: uri.is_valid
has_not_such_map: not has_map (uri.template, rqst_methods, h)
local
l_tpl: like {URI_TEMPLATE}.template
l_uri: URI_TEMPLATE
do
l_uri := based_uri (uri)
l_tpl := l_uri.template
handlers.force ([h, l_tpl, rqst_methods])
templates.force (l_uri, l_tpl)
h.on_handler_mapped (l_tpl, rqst_methods)
end
map_with_request_methods (tpl: READABLE_STRING_8; h: H; rqst_methods: detachable WSF_ROUTER_METHODS)
do
map_with_uri_template_and_request_methods (create {URI_TEMPLATE}.make (tpl), h, rqst_methods)
end
feature {NONE} -- Implementation
based_uri (uri: URI_TEMPLATE): URI_TEMPLATE
do
if attached base_url as l_base_url then
Result := uri.duplicate
Result.set_template (l_base_url + uri.template)
else
Result := uri
end
end
based_resource (a_resource: READABLE_STRING_8): READABLE_STRING_8
do
if attached base_url as l_base_url then
Result := l_base_url + a_resource
else
Result := a_resource
end
end
feature {WSF_ROUTED_SERVICE_I} -- Handler
matching_route (req: WSF_REQUEST): detachable WSF_ROUTE [H, C]
local
l_handlers: like handlers
t: READABLE_STRING_8
p: READABLE_STRING_8
l_req_method: READABLE_STRING_8
l_res: URI_TEMPLATE_MATCH_RESULT
do
p := source_uri (req)
from
l_req_method := request_method (req)
l_handlers := handlers
l_handlers.start
until
l_handlers.after or Result /= Void
loop
if attached l_handlers.item as l_info then
if is_matching_request_methods (l_req_method, l_info.request_methods) then
t := l_info.resource
if
attached {WSF_ROUTING_HANDLER [H, C]} l_info.handler as rah and then
p.starts_with (t)
then
create l_res.make_empty
l_res.path_variables.force (p.substring (t.count + 1, p.count), "path")
create Result.make (l_info.handler, handler_context (p, req, create {URI_TEMPLATE}.make (t), l_res))
elseif attached templates.item (t) as tpl and then
attached tpl.match (p) as res
then
create Result.make (l_info.handler, handler_context (p, req, tpl, res))
end
end
end
l_handlers.forth
end
end
feature {WSF_ROUTED_SERVICE_I} -- Default: implementation
default_handler_context (req: WSF_REQUEST): C
-- <Precursor>
do
Result := handler_context (Void, req, create {URI_TEMPLATE}.make ("/"), create {URI_TEMPLATE_MATCH_RESULT}.make_empty)
end
feature {NONE} -- Context factory
handler_context (p: detachable READABLE_STRING_8; req: WSF_REQUEST; tpl: URI_TEMPLATE; tpl_res: URI_TEMPLATE_MATCH_RESULT): C
do
if p /= Void then
create Result.make (req, tpl, tpl_res, p)
else
create Result.make (req, tpl, tpl_res, source_uri (req))
end
end
feature -- Access: ITERABLE
new_cursor: ITERATION_CURSOR [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]]
-- Fresh cursor associated with current structure
do
Result := handlers.new_cursor
end
feature {NONE} -- Implementation
handlers: ARRAYED_LIST [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]]
-- Handlers indexed by the template expression
-- see `templates'
templates: HASH_TABLE [URI_TEMPLATE, READABLE_STRING_8]
-- URI Template indexed by the template expression
context_path (a_path: STRING): STRING
-- Prepared path from context which match requirement
-- i.e: not empty, starting with '/'
local
p: INTEGER
do
Result := a_path
if Result.is_empty then
Result := "/"
else
if Result[1] /= '/' then
Result := "/" + Result
end
p := Result.index_of ('.', 1)
if p > 0 then
Result := Result.substring (1, p - 1)
end
end
ensure
result_not_empty: not Result.is_empty
end
;note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -1,47 +0,0 @@
note
description: "[
This class helps to build Routing handler based for WSF_URI_TEMPLATE_ROUTER
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_TEMPLATE_ROUTING_HANDLER_I [H -> WSF_HANDLER [C],
C -> WSF_URI_TEMPLATE_HANDLER_CONTEXT create make end]
inherit
WSF_ROUTING_HANDLER [H, C]
create
make,
make_with_base_url
feature {NONE} -- Initialization
make (n: INTEGER)
do
create router.make (n)
end
make_with_base_url (n: INTEGER; a_base_url: like base_url)
-- Make allocated for at least `n' maps,
-- and use `a_base_url' as base_url
do
create router.make_with_base_url (n, a_base_url)
end
feature {NONE} -- Routing
router: WSF_URI_TEMPLATE_ROUTER_I [H, C]
;note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -1,43 +1,69 @@
note
description: "[
Request handler used to respond file system request.
]"
description: "Summary description for {WSF_FILE_SYSTEM_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_FILE_SYSTEM_HANDLER [C -> WSF_HANDLER_CONTEXT]
WSF_FILE_SYSTEM_HANDLER
inherit
WSF_HANDLER [C]
WSF_STARTS_WITH_HANDLER
rename
execute as execute_starts_with
end
create
make
feature {NONE} -- Initialization
make (a_root: READABLE_STRING_8)
make (d: like document_root)
require
a_root_exists: node_exists (a_root)
valid_d: (d /= Void and then not d.is_empty) implies d.ends_with ("/")
local
e: EXECUTION_ENVIRONMENT
do
document_root := a_root
if d.is_empty then
create e
document_root := e.current_working_directory
else
document_root := d
end
end
feature -- Access
feature -- Access
document_root: READABLE_STRING_8
-- Document root for the file system
document_root: STRING
max_age: INTEGER
index_disabled: BOOLEAN
-- Index disabled?
directory_index: detachable ARRAY [READABLE_STRING_8]
-- File serve if a directory index is requested
not_found_handler: detachable PROCEDURE [ANY, TUPLE [uri: READABLE_STRING_8; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE]]
access_denied_handler: detachable PROCEDURE [ANY, TUPLE [uri: READABLE_STRING_8; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE]]
not_found_handler: detachable PROCEDURE [ANY, TUPLE [uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE]]
access_denied_handler: detachable PROCEDURE [ANY, TUPLE [uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE]]
feature -- Element change
enable_index
do
index_disabled := False
end
disable_index
do
index_disabled := True
end
set_max_age (a_seconds: like max_age)
do
max_age := a_seconds
end
set_directory_index (idx: like directory_index)
-- Set `directory_index' as `idx'
do
@@ -62,38 +88,25 @@ feature -- Element change
feature -- Execution
requested_path (ctx: C): detachable READABLE_STRING_8
-- Path associated with the request
-- i.e: path of the file system resource if any
do
if attached {WSF_STRING} ctx.item ("path") as v_path then
Result := v_path.value.as_string_8
end
end
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
execute (a_start_path: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
local
h: HTTP_HEADER
s: STRING
p: STRING
do
if attached requested_path (ctx) as uri then
process_uri (uri, ctx, req, res)
p := req.path_info
if p.starts_with (a_start_path) then
p.remove_head (a_start_path.count)
else
s := "Hello " + ctx.path + "%N"
s.append ("root=" + document_root)
create h.make
h.put_content_type_text_html
h.put_content_length (s.count)
res.set_status_code ({HTTP_STATUS_CODE}.ok)
res.put_header_text (h.string)
res.put_string (s)
check starts_with_base: False end
end
process_uri (p, req, res)
end
process_uri (uri: READABLE_STRING_8; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
execute_starts_with (a_start_path: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
do
execute (a_start_path,req, res)
end
process_uri (uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
local
f: RAW_FILE
fn: READABLE_STRING_8
@@ -103,19 +116,23 @@ feature -- Execution
if f.exists then
if f.is_readable then
if f.is_directory then
respond_index (req.request_uri, fn, ctx, req, res)
if index_disabled then
process_directory_index_disabled (uri, req, res)
else
process_index (req.request_uri, fn, req, res)
end
else
respond_file (f, ctx, req, res)
process_file (f, req, res)
end
else
respond_access_denied (uri, ctx, req, res)
process_access_denied (uri, req, res)
end
else
respond_not_found (uri, ctx, req, res)
process_not_found (uri, req, res)
end
end
respond_index (a_uri: READABLE_STRING_8; dn: READABLE_STRING_8; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
process_index (a_uri: READABLE_STRING_8; dn: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
local
h: HTTP_HEADER
uri, s: STRING_8
@@ -124,7 +141,7 @@ feature -- Execution
do
create d.make_open_read (dn)
if attached directory_index_file (d) as f then
respond_file (f, ctx, req, res)
process_file (f, req, res)
else
uri := a_uri
if not uri.is_empty and then uri [uri.count] /= '/' then
@@ -170,31 +187,72 @@ feature -- Execution
d.close
end
respond_file (f: FILE; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
process_file (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE)
local
ext: READABLE_STRING_8
ct: detachable READABLE_STRING_8
fres: WSF_FILE_RESPONSE
dt: DATE_TIME
do
ext := extension (f.name)
ct := extension_mime_mapping.mime_type (ext)
if ct = Void then
ct := {HTTP_MIME_TYPES}.application_force_download
end
create fres.make_with_content_type (ct, f.name)
fres.set_status_code ({HTTP_STATUS_CODE}.ok)
fres.set_answer_head_request_method (req.request_method.same_string ({HTTP_REQUEST_METHODS}.method_head))
if
attached req.meta_string_variable ("HTTP_IF_MODIFIED_SINCE") as s_if_modified_since and then
attached file_date (f) as f_date and then (f_date >= rfc1123_http_date_format_to_date (s_if_modified_since))
then
process_not_modified (f_date, req, res)
else
create fres.make_with_content_type (ct, f.name)
fres.set_status_code ({HTTP_STATUS_CODE}.ok)
res.send (fres)
-- cache control
create dt.make_now_utc
fres.header.put_cache_control ("private, max-age=" + max_age.out)
fres.header.put_utc_date (dt)
if max_age > 0 then
dt := dt.twin
dt.second_add (max_age)
end
fres.header.put_expires_date (dt)
fres.set_answer_head_request_method (req.request_method.same_string ({HTTP_REQUEST_METHODS}.method_head))
res.send (fres)
end
end
respond_not_found (uri: READABLE_STRING_8; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
process_not_modified (a_utc_date: detachable DATE_TIME; req: WSF_REQUEST; res: WSF_RESPONSE)
local
h: HTTP_HEADER
dt: DATE_TIME
do
create dt.make_now_utc
create h.make
h.put_cache_control ("private, max-age=" + max_age.out)
h.put_utc_date (dt)
if max_age > 0 then
dt := dt.twin
dt.second_add (max_age)
end
h.put_expires_date (dt)
if a_utc_date /= Void then
h.put_last_modified (a_utc_date)
end
res.set_status_code ({HTTP_STATUS_CODE}.not_modified)
res.put_header_text (h.string)
res.flush
end
process_not_found (uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
local
h: HTTP_HEADER
s: STRING_8
do
if attached not_found_handler as hdl then
hdl.call ([uri, ctx, req, res])
hdl.call ([uri, req, res])
else
create h.make
h.put_content_type_text_plain
@@ -208,13 +266,13 @@ feature -- Execution
end
end
respond_access_denied (uri: READABLE_STRING_8; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
process_access_denied (uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
local
h: HTTP_HEADER
s: STRING_8
do
if attached access_denied_handler as hdl then
hdl.call ([uri, ctx, req, res])
hdl.call ([uri, req, res])
else
create h.make
h.put_content_type_text_plain
@@ -228,6 +286,26 @@ feature -- Execution
end
end
process_directory_index_disabled (uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
local
h: HTTP_HEADER
s: STRING_8
do
if attached access_denied_handler as hdl then
hdl.call ([uri, req, res])
else
create h.make
h.put_content_type_text_plain
create s.make_empty
s.append ("Directory index: Access denied%N")
res.set_status_code ({HTTP_STATUS_CODE}.forbidden)
h.put_content_length (s.count)
res.put_header_text (h.string)
res.put_string (s)
res.flush
end
end
feature {NONE} -- Implementation
directory_index_file (d: DIRECTORY): detachable FILE
@@ -343,14 +421,33 @@ feature {NONE} -- Implementation
end
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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
]"
feature {NONE} -- implementation: date time
date_time_utility: HTTP_DATE_TIME_UTILITIES
once
create Result
end
file_date (f: FILE): DATE_TIME
do
Result := timestamp_to_date (f.date)
end
rfc1123_http_date_format_to_date (s: STRING): DATE_TIME
-- String representation of `dt' using the RFC 1123
local
t: STRING
do
t := s
if t.ends_with ("GMT") then
t := t.substring (1, t.count - 4)
end
create Result.make_from_string (t, "ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2")
end
timestamp_to_date (n: INTEGER): DATE_TIME
do
Result := date_time_utility.unix_time_stamp_to_date_time (n)
end
end

View File

@@ -1,20 +1,11 @@
note
description: "[
Request handler object which is called by a WSF_ROUTER
An handler should implement the method
execute (ctx, req, res)
The class is generic, this way one can use a custom WSF_HANDLER_CONTEXT if needed
]"
description: "Summary description for {WSF_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_HANDLER [C -> WSF_HANDLER_CONTEXT]
inherit
ANY
WSF_HANDLER
feature -- Status report
@@ -23,60 +14,10 @@ feature -- Status report
do
Result := True
end
feature {WSF_ROUTER} -- Mapping change
feature -- Execution
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
--
-- `ctx': contains advanced data related to request_uri
-- in the case of URI_TEMPLATE, it add support for "path_parameter"
-- `req': request data
-- `res': reponse stream
--| note `ctx' can also provide data coming from `req'
require
is_valid_context: is_valid_context (req)
deferred
ensure
response_status_set: res.status_is_set
end
feature -- Execution: report
url (req: WSF_REQUEST; a_base: detachable READABLE_STRING_8; args: detachable STRING; abs: BOOLEAN): STRING
-- Associated url based on `a_base' and `args'
-- if `abs' then return absolute url
local
s: detachable STRING
l_base: STRING
do
if a_base /= Void then
l_base := a_base
else
l_base := req.request_uri
end
s := args
if s /= Void and then s.count > 0 then
if s[1] /= '/' then
s := l_base + "/" + s
else
s := l_base + s
end
else
s := l_base
end
if abs then
Result := req.absolute_script_url (s)
else
Result := req.script_url (s)
end
ensure
result_attached: Result /= Void
end
feature {WSF_ROUTER} -- Routes change
on_handler_mapped (a_resource: READABLE_STRING_8; a_rqst_methods: detachable WSF_ROUTER_METHODS)
on_mapped (a_mapping: WSF_ROUTER_MAPPING; a_rqst_methods: detachable WSF_ROUTER_METHODS)
-- Callback called when a router map a route to Current handler
do
end

View File

@@ -1,45 +0,0 @@
note
description: "[
a WSF_ROUTE object associates a handler and the associated context at runtime
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_ROUTE [H -> WSF_HANDLER [C], C -> WSF_HANDLER_CONTEXT]
create
make
feature {NONE} -- Initialization
make (h: H; c: C)
-- Instantiate Current with `h' and `c'
do
handler := h
context := c
end
feature -- Access
handler: H
-- Handler
context: C
-- Context associated to `handler' for execution
invariant
handler /= Void
context /= Void
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -1,111 +1,120 @@
note
description: "[
URL dispatcher/router based on deferred mapping (to be defined in descendant)
The associated context {WSF_HANDLER_CONTEXT} does contains information related to the matching at runtime.
]"
description: "Summary description for {EWF_ROUTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_ROUTER [H -> WSF_HANDLER [C], C -> WSF_HANDLER_CONTEXT]
class
WSF_ROUTER
inherit
ITERABLE [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]]
ITERABLE [TUPLE [mapping: WSF_ROUTER_MAPPING; request_methods: detachable WSF_ROUTER_METHODS]]
create
make,
make_with_base_url
feature {NONE} -- Initialization
make (n: INTEGER)
do
create mappings.make (n)
initialize
end
make_with_base_url (n: INTEGER; a_base_url: like base_url)
-- Make router allocated for at least `n' maps,
-- and use `a_base_url' as base_url
do
make (n)
set_base_url (a_base_url)
end
initialize
-- Initialize router
do
create pre_route_execution_actions
create mappings.make (10)
create pre_execution_actions
end
feature -- Status report
has_map (a_resource: READABLE_STRING_8; rqst_methods: detachable WSF_ROUTER_METHODS; a_handler: detachable H): BOOLEAN
-- Has a map corresponding to `a_resource' and `rqst_methods' other than `a_handler'?
do
if attached handlers_matching_map (a_resource, rqst_methods) as lst then
Result := a_handler = Void or else not lst.has (a_handler)
end
end
handlers_matching_map (a_resource: READABLE_STRING_8; rqst_methods: detachable WSF_ROUTER_METHODS): detachable LIST [H]
-- Existing handlers matching map with `a_resource' and `rqst_methods'
deferred
end
mappings: ARRAYED_LIST [TUPLE [mapping: WSF_ROUTER_MAPPING; request_methods: detachable WSF_ROUTER_METHODS]]
-- Existing mappings
feature -- Mapping
map (a_resource: READABLE_STRING_8; h: H)
-- Map handler `h' with `a_resource'
require
has_not_such_map: not has_map (a_resource, Void, h)
map (a_mapping: WSF_ROUTER_MAPPING)
-- Map `a_mapping'
do
map_with_request_methods (a_resource, h, Void)
map_with_request_methods (a_mapping, Void)
end
map_routing (a_resource: READABLE_STRING_8; h: H)
-- Map handler `h' with `a_resource'
require
has_not_such_map: not has_map (a_resource, Void, h)
map_with_request_methods (a_mapping: WSF_ROUTER_MAPPING; rqst_methods: detachable WSF_ROUTER_METHODS)
-- Map `a_mapping' for request methods `rqst_methods'
do
map (a_resource, h)
mappings.extend ([a_mapping, rqst_methods])
a_mapping.handler.on_mapped (a_mapping, rqst_methods)
end
map_with_request_methods (a_resource: READABLE_STRING_8; h: H; rqst_methods: detachable WSF_ROUTER_METHODS)
-- Map handler `h' with `a_resource' and `rqst_methods'
require
has_not_such_map: not has_map (a_resource, rqst_methods, h)
deferred
end
feature -- Access
feature -- Mapping agent
is_dispatched: BOOLEAN
-- `dispatch' set `is_dispatched' to True
-- if handler was found and executed
map_agent (a_resource: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE]])
-- Map `a_action' as an handler with `a_resource'
dispatch (req: WSF_REQUEST; res: WSF_RESPONSE)
do
map_agent_with_request_methods (a_resource, a_action, Void)
end
map_agent_with_request_methods (a_resource: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE]];
rqst_methods: detachable WSF_ROUTER_METHODS)
-- Map `a_action' as an handler with `a_resource' and `rqst_methods'
local
rah: WSF_AGENT_HANDLER [C]
do
create rah.make (a_action)
if attached {H} rah as h then
map_with_request_methods (a_resource, h, rqst_methods)
else
check valid_agent_handler: False end
if attached dispatch_and_return_handler (req, res) then
check is_dispatched: is_dispatched end
end
end
feature -- Mapping response agent
map_agent_response (a_resource: READABLE_STRING_8; a_function: FUNCTION [ANY, TUPLE [ctx: C; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE])
-- Map response as Result `a_function' as an handler with `a_resource'
do
map_agent_response_with_request_methods (a_resource, a_function, Void)
end
map_agent_response_with_request_methods (a_resource: READABLE_STRING_8; a_function: FUNCTION [ANY, TUPLE [ctx: C; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE];
rqst_methods: detachable WSF_ROUTER_METHODS)
-- Map response as Result `a_function' as an handler with `a_resource' and `rqst_methods'
dispatch_and_return_handler (req: WSF_REQUEST; res: WSF_RESPONSE): detachable WSF_HANDLER
local
rah: WSF_AGENT_RESPONSE_HANDLER [C]
l_req_method: READABLE_STRING_8
m: WSF_ROUTER_MAPPING
do
create rah.make (a_function)
if attached {H} rah as h then
map_with_request_methods (a_resource, h, rqst_methods)
else
check valid_agent_handler: False end
is_dispatched := False
l_req_method := request_method (req)
across
mappings as c
until
Result /= Void
loop
if attached c.item as l_info then
if is_matching_request_methods (l_req_method, l_info.request_methods) then
m := l_info.mapping
if attached m.routed_handler (req, res, Current) as r then
is_dispatched := True
Result := r
end
end
end
end
end
feature -- Hook
execute_before (a_mapping: WSF_ROUTER_MAPPING)
do
pre_execution_actions.call ([a_mapping])
end
execute_after (a_mapping: WSF_ROUTER_MAPPING)
do
end
pre_execution_actions: ACTION_SEQUENCE [TUPLE [WSF_ROUTER_MAPPING]]
-- Action triggered before a route is execute
--| Could be used for tracing, logging
feature -- Base url
count: INTEGER
do
Result := mappings.count
end
base_url: detachable READABLE_STRING_8
-- Common start of any route url
@@ -115,6 +124,7 @@ feature -- Element change
-- Set `base_url' to `a_base_url'
-- make sure no map is already added (i.e: count = 0)
require
a_valid_base_url: (a_base_url /= Void and then a_base_url.is_empty) implies (a_base_url.starts_with ("/") and not a_base_url.ends_with ("/"))
no_handler_set: count = 0
do
if a_base_url = Void or else a_base_url.is_empty then
@@ -124,105 +134,12 @@ feature -- Element change
end
end
feature -- Hook
pre_route_execution_actions: ACTION_SEQUENCE [TUPLE [like route]]
-- Action triggered before a route is execute
--| Could be used for tracing, logging
feature -- Routing
route (req: WSF_REQUEST): detachable WSF_ROUTE [H, C]
-- Route matching `req'.
do
Result := matching_route (req)
end
feature -- Execution
execute_route (a_route: WSF_ROUTE [H,C]; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Process route `a_route'
require
a_route_attached: a_route /= Void
local
ctx: C
do
pre_route_execution_actions.call ([a_route])
ctx := a_route.context
ctx.apply (req)
a_route.handler.execute (ctx, req, res)
ctx.revert (req)
rescue
a_route.context.revert (req)
end
dispatch (req: WSF_REQUEST; res: WSF_RESPONSE): BOOLEAN
-- Dispatch `req, res' to the associated handler
-- And return True is handled, otherwise False
do
if attached route (req) as r then
Result := True
execute_route (r, req, res)
end
end
dispatch_and_return_handler (req: WSF_REQUEST; res: WSF_RESPONSE): detachable H
-- Dispatch `req, res' to the associated handler
-- And return this handler
-- If Result is Void, this means no handler was found.
do
if attached route (req) as r then
Result := r.handler
execute_route (r, req, res)
end
end
feature {WSF_ROUTED_SERVICE_I} -- Implementation
default_handler_context (req: WSF_REQUEST): C
-- Default handler context associated with `req'.
--| It can be used to build a context if needed.
deferred
end
feature -- status report
count: INTEGER
-- Count of maps handled by current
do
across
Current as curs
loop
if attached {WSF_ROUTING_HANDLER [H, C]} curs.item.handler as rh then
Result := Result + rh.count + 1 --| +1 for the handler itself
else
Result := Result + 1
end
end
end
feature -- Traversing
new_cursor: ITERATION_CURSOR [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]]
new_cursor: ITERATION_CURSOR [TUPLE [mapping: WSF_ROUTER_MAPPING; request_methods: detachable WSF_ROUTER_METHODS]]
-- Fresh cursor associated with current structure
deferred
end
feature {WSF_ROUTED_SERVICE_I} -- Handler
source_uri (req: WSF_REQUEST): READABLE_STRING_32
-- URI to use to find handler.
do
Result := req.path_info
end
matching_route (req: WSF_REQUEST): detachable WSF_ROUTE [H, C]
-- Handler whose map matched with `req' with associated Context
require
req_valid: source_uri (req) /= Void
deferred
ensure
source_uri_unchanged: source_uri (req).same_string (old source_uri (req))
Result := mappings.new_cursor
end
feature -- Request methods helper
@@ -343,14 +260,4 @@ feature {NONE} -- Access: Implementation
end
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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

@@ -1,31 +1,33 @@
note
description: "Summary description for WSF_URI_ROUTED_SERVICE."
description: "Summary description for {WSF_ROUTER_MAPPING}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_URI_ROUTED_SERVICE
WSF_ROUTER_MAPPING
inherit
WSF_ROUTED_SERVICE_I [WSF_HANDLER [WSF_URI_HANDLER_CONTEXT], WSF_URI_HANDLER_CONTEXT]
redefine
router
feature -- Access
handler: WSF_HANDLER
deferred
end
feature {NONE} -- Initialization
feature -- Status
create_router
-- Create router
--| it can be redefine to create with precise count if needed.
routed_handler (req: WSF_REQUEST; res: WSF_RESPONSE; a_router: WSF_ROUTER): detachable WSF_HANDLER
deferred
end
feature -- Helper
source_uri (req: WSF_REQUEST): READABLE_STRING_32
-- URI to use to find handler.
do
create router.make (0)
Result := req.path_info
end
feature -- Router
router: WSF_URI_ROUTER
;note
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[

View File

@@ -1,113 +0,0 @@
note
description: "[
WSF_ROUTING_HANDLER is mainly to group a set of handler having the same base
such as /users for
/users/by/id/{id}
/users/by/name/name}
It can be used to optimize the router, where the router checks only the base path before checking each entries
Then for
/a/a1
/a/a2
/a/a3
/a/a4
/b/b1
/b/b2
/b/b3
2 routing handlers could be used "/a" and "/b"
then to find the /b/b2 match, the router has to do only
/a /b /b/b1 and /b/b2 i.e: 4 checks
instead of /a/a1 /a/a2 /a/a3 /a/a4 /b/b1 /b/b2: i.e: 6 checks
On router with deep arborescence this could be significant
]"
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_ROUTING_HANDLER [H -> WSF_HANDLER [C], C -> WSF_HANDLER_CONTEXT]
inherit
WSF_HANDLER [C]
feature -- Access
count: INTEGER
-- Count of maps handled by current
do
Result := router.count
end
base_url: detachable READABLE_STRING_8
do
Result := router.base_url
end
feature -- Element change
set_base_url (a_base_url: like base_url)
-- Set `base_url' to `a_base_url'
-- make sure no map is already added (i.e: count = 0)
require
no_handler_set: count = 0
do
router.set_base_url (a_base_url)
end
feature -- Execution
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
local
r: detachable WSF_ROUTE [H, C]
do
r := router.route (req)
if r = Void then
res.put_header ({HTTP_STATUS_CODE}.not_found, <<[{HTTP_HEADER_NAMES}.header_content_length, "0"]>>)
else
router.execute_route (r, req, res)
end
end
feature {NONE} -- Routing
router: WSF_ROUTER [H, C]
deferred
end
feature -- Mapping
map (a_resource: READABLE_STRING_8; h: H)
-- Map handler `h' with `a_resource'
do
router.map (a_resource, h)
end
map_with_request_methods (a_resource: READABLE_STRING_8; h: H;
rqst_methods: detachable WSF_ROUTER_METHODS)
-- Map handler `h' with `a_resource' and `rqst_methods'
do
router.map_with_request_methods (a_resource, h, rqst_methods)
end
map_agent (a_resource: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE]])
do
router.map_agent (a_resource, a_action)
end
map_agent_with_request_methods (a_resource: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE]];
rqst_methods: detachable WSF_ROUTER_METHODS)
do
router.map_agent_with_request_methods (a_resource, a_action, rqst_methods)
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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