diff --git a/library/server/request/router/doc/README.txt b/library/server/request/router/doc/README.txt
new file mode 100644
index 00000000..78339059
--- /dev/null
+++ b/library/server/request/router/doc/README.txt
@@ -0,0 +1,14 @@
+This library introduce the notion of router and handler
+The Router manages the association between URI,URI template and Handler
+The Router is in charge to route/dispatch a request to one of the Handler according to the URI and how the Handler is mapped in the Router.
+
+Common usage
+
+router: REQUEST_ROUTER
+hello_handler: REQUEST_HANDLER
+
+create {REQUEST_URI_TEMPLATE_ROUTER} router.make
+create {REQUEST_AGENT_HANDLER} hello_handler.make (agent handle_hello)
+
+router.map ("/hello/{name}", hello_handler)
+router.map_agent ("/hello/{name}", agent handle_hello)
\ No newline at end of file
diff --git a/library/server/request/router/license.lic b/library/server/request/router/license.lic
new file mode 100644
index 00000000..cf2d1ed9
--- /dev/null
+++ b/library/server/request/router/license.lic
@@ -0,0 +1,10 @@
+${NOTE_KEYWORD}
+ copyright: "2011-${YEAR}, 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
+ ]"
diff --git a/library/server/request/router/router-safe.ecf b/library/server/request/router/router-safe.ecf
new file mode 100644
index 00000000..48d0b68b
--- /dev/null
+++ b/library/server/request/router/router-safe.ecf
@@ -0,0 +1,20 @@
+
+
+
+
+
+ /.git$
+ /EIFGENs$
+ /.svn$
+
+
+
+
+
+
+
+
+
+
diff --git a/library/server/request/router/router.ecf b/library/server/request/router/router.ecf
new file mode 100644
index 00000000..48d0b68b
--- /dev/null
+++ b/library/server/request/router/router.ecf
@@ -0,0 +1,20 @@
+
+
+
+
+
+ /.git$
+ /EIFGENs$
+ /.svn$
+
+
+
+
+
+
+
+
+
+
diff --git a/library/server/request/router/src/context/request_handler_context.e b/library/server/request/router/src/context/request_handler_context.e
new file mode 100644
index 00000000..97f404d8
--- /dev/null
+++ b/library/server/request/router/src/context/request_handler_context.e
@@ -0,0 +1,71 @@
+note
+ description: "Summary description for {REQUEST_HANDLER_CONTEXT}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+deferred class
+ REQUEST_HANDLER_CONTEXT
+
+feature -- Access
+
+ request: EWSGI_REQUEST
+ -- Associated request
+
+ path: STRING
+ -- ???
+
+ request_format: detachable STRING
+ -- Request format based on `Content-Type'.
+ local
+ s: detachable STRING
+ do
+ s := request.environment.content_type
+ if s = Void then
+ elseif s.same_string ({HTTP_CONSTANTS}.json_text) then
+ Result := {HTTP_FORMAT_CONSTANTS}.json_name
+ elseif s.same_string ({HTTP_CONSTANTS}.json_app) then
+ Result := {HTTP_FORMAT_CONSTANTS}.json_name
+ elseif s.same_string ({HTTP_CONSTANTS}.xml_text) then
+ Result := {HTTP_FORMAT_CONSTANTS}.xml_name
+ elseif s.same_string ({HTTP_CONSTANTS}.html_text) then
+ Result := {HTTP_FORMAT_CONSTANTS}.html_name
+ elseif s.same_string ({HTTP_CONSTANTS}.plain_text) then
+ Result := {HTTP_FORMAT_CONSTANTS}.text_name
+ end
+ end
+
+feature -- Query
+
+ path_parameter (a_name: STRING): detachable STRING_32
+ -- Parameter value for path variable `a_name'
+ deferred
+ end
+
+ query_parameter (a_name: STRING): detachable STRING_32
+ -- Parameter value for query variable `a_name'
+ --| i.e after the ? character
+ deferred
+ end
+
+ parameter (a_name: STRING): detachable STRING_32
+ -- Any parameter value for variable `a_name'
+ -- URI template parameter and query parameters
+ do
+ Result := query_parameter (a_name)
+ if Result = Void then
+ Result := path_parameter (a_name)
+ end
+ end
+
+;note
+ copyright: "2011-2011, 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/request/router/src/context/request_uri_handler_context.e b/library/server/request/router/src/context/request_uri_handler_context.e
new file mode 100644
index 00000000..1cbecbce
--- /dev/null
+++ b/library/server/request/router/src/context/request_uri_handler_context.e
@@ -0,0 +1,45 @@
+note
+ description: "Summary description for {REQUEST_URI_HANDLER_CONTEXT}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ REQUEST_URI_HANDLER_CONTEXT
+
+inherit
+ REQUEST_HANDLER_CONTEXT
+
+create
+ make
+
+feature {NONE} -- Initialization
+
+ make (req: EWSGI_REQUEST; p: like path)
+ do
+ request := req
+ path := p
+ end
+
+feature -- Query
+
+ path_parameter (a_name: STRING): detachable STRING_32
+ do
+ end
+
+ query_parameter (a_name: STRING): detachable STRING_32
+ do
+ Result := request.parameter (a_name)
+ end
+
+note
+ copyright: "2011-2011, 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/request/router/src/context/request_uri_template_handler_context.e b/library/server/request/router/src/context/request_uri_template_handler_context.e
new file mode 100644
index 00000000..678e35b8
--- /dev/null
+++ b/library/server/request/router/src/context/request_uri_template_handler_context.e
@@ -0,0 +1,57 @@
+note
+ description: "Summary description for {REQUEST_URI_TEMPLATE_HANDLER_CONTEXT}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ REQUEST_URI_TEMPLATE_HANDLER_CONTEXT
+
+inherit
+ REQUEST_HANDLER_CONTEXT
+
+create
+ make
+
+feature {NONE} -- Initialization
+
+ make (req: EWSGI_REQUEST; tpl: URI_TEMPLATE; tpl_res: URI_TEMPLATE_MATCH_RESULT; p: like path)
+ do
+ request := req
+ uri_template := tpl
+ uri_template_match := tpl_res
+ path := p
+ end
+
+feature -- Access
+
+ uri_template: URI_TEMPLATE
+
+ uri_template_match: URI_TEMPLATE_MATCH_RESULT
+
+feature -- Query
+
+ path_parameter (a_name: STRING): detachable STRING_32
+ do
+ Result := uri_template_match.url_decoded_path_variable (a_name)
+ end
+
+ query_parameter (a_name: STRING): detachable STRING_32
+ do
+ Result := uri_template_match.url_decoded_query_variable (a_name)
+ if Result = Void then
+ Result := request.parameter (a_name)
+ end
+ end
+
+note
+ copyright: "2011-2011, 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/request/router/src/handler/request_agent_handler.e b/library/server/request/router/src/handler/request_agent_handler.e
new file mode 100644
index 00000000..f95f0b8c
--- /dev/null
+++ b/library/server/request/router/src/handler/request_agent_handler.e
@@ -0,0 +1,45 @@
+note
+ description: "Summary description for REQUEST_AGENT_HANDLER."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ REQUEST_AGENT_HANDLER
+
+inherit
+ REQUEST_HANDLER
+
+create
+ make
+
+feature -- Initialization
+
+ make (act: like action)
+ do
+ action := act
+ initialize
+ end
+
+feature -- Access
+
+ action: PROCEDURE [ANY, TUPLE [ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM]]
+
+feature -- Execution
+
+ execute_application (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM)
+ do
+ action.call ([ctx, req, res])
+ end
+
+note
+ copyright: "2011-2011, 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/request/router/src/handler/request_handler.e b/library/server/request/router/src/handler/request_handler.e
new file mode 100644
index 00000000..fe4525bd
--- /dev/null
+++ b/library/server/request/router/src/handler/request_handler.e
@@ -0,0 +1,310 @@
+note
+ description: "Summary description for {REQUEST_HANDLER}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+deferred class
+ REQUEST_HANDLER
+
+feature {NONE} -- Initialization
+
+ initialize
+ -- Initialize various attributes
+ do
+ end
+
+feature -- Access
+
+ description: detachable STRING
+ -- Optional descriptiong
+
+feature -- Status report
+
+ is_valid_context (req: EWSGI_REQUEST): BOOLEAN
+ -- Is `req' valid context for current handler?
+ do
+ Result := request_method_name_supported (req.environment.request_method)
+ end
+
+feature -- Execution
+
+ execute (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM)
+ -- Execute request handler
+ require
+ is_valid_context: is_valid_context (req)
+ local
+ rescued: BOOLEAN
+ do
+ if not rescued then
+ if request_method_name_supported (req.environment.request_method) then
+ pre_execute (req)
+ execute_application (a_hdl_context, req, res)
+ post_execute (req, res)
+ else
+ execute_method_not_allowed (a_hdl_context, req, res)
+ end
+ else
+ rescue_execute (req, res)
+ end
+ rescue
+ rescued := True
+ retry
+ end
+
+ execute_method_not_allowed (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM)
+ local
+ s: STRING
+ lst: LIST [STRING]
+ do
+ res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed)
+ create s.make (25)
+ from
+ lst := supported_request_method_names
+ lst.start
+ until
+ lst.after
+ loop
+ s.append_string (lst.item)
+ if not lst.islast then
+ s.append_character (',')
+ s.append_character (' ')
+ end
+ lst.forth
+ end
+ res.write_header ({HTTP_STATUS_CODE}.method_not_allowed, <<["Allow", s]>>)
+ end
+
+ execute_application (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM)
+ -- Execute request handler
+ deferred
+ end
+
+ pre_execute (req: EWSGI_REQUEST)
+ -- Operation processed before `execute'
+ do
+ --| To be redefined if needed
+ end
+
+ post_execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM)
+ -- Operation processed after `execute'
+ do
+ --| To be redefined if needed
+ end
+
+ rescue_execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM)
+ -- Operation processed after a rescue
+ do
+ --| To be redefined if needed
+ post_execute (req, res)
+ end
+
+feature -- Execution: report
+
+-- execution_information (req: EWSGI_REQUEST): detachable REQUEST_HANDLER_CONTEXT
+-- -- Execution information related to the request
+-- do
+-- if attached path_information (req, req.environment.path_info) as info then
+-- create Result.make (req.environment.path_info)
+-- end
+-- end
+
+-- path_information (req: EWSGI_REQUEST; a_rq_path: STRING): detachable TUPLE [format: detachable STRING; arguments: detachable STRING]
+-- -- Information related to `a_path'
+-- local
+-- l_rq_path: STRING
+-- i,p,n: INTEGER
+-- l_format, l_args: detachable STRING
+-- do
+-- l_rq_path := a_rq_path
+-- if l_rq_path.count > 0 and then l_rq_path[1] /= '/' then
+-- l_rq_path := "/" + l_rq_path
+-- end
+-- n := l_rq_path.count
+-- i := req.environment.path_info.count + 1
+
+-- if format_located_before_parameters then
+-- --| path = app-path{.format}/parameters
+
+-- if l_rq_path.valid_index (i) and then l_rq_path[i] = '.' then
+-- p := l_rq_path.index_of ('/', i + 1)
+-- if p = 0 then
+-- p := n + 1
+-- else
+-- l_args := l_rq_path.substring (p + 1, n)
+-- end
+-- l_format := l_rq_path.substring (i + 1, p - 1)
+-- elseif n > i then
+-- check l_rq_path[i] = '/' end
+-- l_args := l_rq_path.substring (i + 1, n)
+-- end
+-- elseif format_located_after_parameters then
+-- --| path = app-path/parameters{.format}
+
+-- p := l_rq_path.last_index_of ('.', n)
+-- if p > i then
+-- l_format := l_rq_path.substring (p + 1, n)
+-- l_args := l_rq_path.substring (i + 1, p - 1)
+-- elseif n > i then
+-- check l_rq_path[i] = '/' end
+-- l_format := Void
+-- l_args := l_rq_path.substring (i + 1, n)
+-- end
+-- end
+-- if l_format /= Void or l_args /= Void then
+-- Result := [l_format, l_args]
+-- end
+-- end
+
+ url (req: EWSGI_REQUEST; args: detachable STRING; abs: BOOLEAN): STRING
+ -- Associated url based on `path' and `args'
+ -- if `abs' then return absolute url
+ local
+ s: detachable STRING
+ do
+ s := args
+ if s /= Void and then s.count > 0 then
+ if s[1] /= '/' then
+ s := req.environment.request_uri + "/" + s
+ else
+ s := req.environment.request_uri + s
+ end
+ else
+ s := req.environment.request_uri
+ end
+ if abs then
+ Result := req.absolute_script_url (s)
+ else
+ Result := req.script_url (s)
+ end
+ ensure
+ result_attached: Result /= Void
+ end
+
+feature -- Element change
+
+ set_description (s: like description)
+ -- Set `description' to `s'
+ do
+ description := s
+ end
+
+feature {NONE} -- Implementation
+
+ supported_request_methods: INTEGER
+ -- Support request method such as GET, POST, ...
+
+feature {NONE} -- Status report
+
+ request_method_id_supported (a_id: INTEGER): BOOLEAN
+ do
+ Result := (supported_request_methods & a_id) = a_id
+ end
+
+ request_method_name_supported (n: STRING): BOOLEAN
+ -- Is request method `n' supported?
+ do
+ Result := request_method_id_supported (request_method_constants.method_id (n))
+ end
+
+ request_method_constants: HTTP_REQUEST_METHOD_CONSTANTS
+ once
+ create Result
+ end
+
+feature -- Status report
+
+ supported_request_method_names: LIST [STRING]
+ -- Support request method such as GET, POST, ...
+ do
+ create {LINKED_LIST [STRING]} Result.make
+ if method_get_supported then
+ Result.extend (request_method_constants.method_get_name)
+ end
+ if method_post_supported then
+ Result.extend (request_method_constants.method_post_name)
+ end
+ if method_put_supported then
+ Result.extend (request_method_constants.method_put_name)
+ end
+ if method_delete_supported then
+ Result.extend (request_method_constants.method_delete_name)
+ end
+ if method_head_supported then
+ Result.extend (request_method_constants.method_head_name)
+ end
+ end
+
+ method_get_supported: BOOLEAN
+ do
+ Result := request_method_id_supported ({HTTP_REQUEST_METHOD_CONSTANTS}.method_get)
+ end
+
+ method_post_supported: BOOLEAN
+ do
+ Result := request_method_id_supported ({HTTP_REQUEST_METHOD_CONSTANTS}.method_post)
+ end
+
+ method_put_supported: BOOLEAN
+ do
+ Result := request_method_id_supported ({HTTP_REQUEST_METHOD_CONSTANTS}.method_put)
+ end
+
+ method_delete_supported: BOOLEAN
+ do
+ Result := request_method_id_supported ({HTTP_REQUEST_METHOD_CONSTANTS}.method_delete)
+ end
+
+ method_head_supported: BOOLEAN
+ do
+ Result := request_method_id_supported ({HTTP_REQUEST_METHOD_CONSTANTS}.method_head)
+ end
+
+feature -- Element change: request methods
+
+ reset_supported_request_methods
+ do
+ supported_request_methods := 0
+ end
+
+ enable_request_method_get
+ do
+ enable_request_method ({HTTP_REQUEST_METHOD_CONSTANTS}.method_get)
+ end
+
+ enable_request_method_post
+ do
+ enable_request_method ({HTTP_REQUEST_METHOD_CONSTANTS}.method_post)
+ end
+
+ enable_request_method_put
+ do
+ enable_request_method ({HTTP_REQUEST_METHOD_CONSTANTS}.method_put)
+ end
+
+ enable_request_method_delete
+ do
+ enable_request_method ({HTTP_REQUEST_METHOD_CONSTANTS}.method_delete)
+ end
+
+ enable_request_method_head
+ do
+ enable_request_method ({HTTP_REQUEST_METHOD_CONSTANTS}.method_head)
+ end
+
+ enable_request_method (m: INTEGER)
+ do
+ supported_request_methods := supported_request_methods | m
+ end
+
+note
+ copyright: "2011-2011, 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/request/router/src/router/request_router.e b/library/server/request/router/src/router/request_router.e
new file mode 100644
index 00000000..eef95ac6
--- /dev/null
+++ b/library/server/request/router/src/router/request_router.e
@@ -0,0 +1,87 @@
+note
+ description: "Summary description for {REQUEST_ROUTER}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+deferred class
+ REQUEST_ROUTER
+
+feature -- Registration
+
+ map_default (r: like default_handler)
+ -- Map default handler
+ -- If no route/handler is found,
+ -- then use `default_handler' as default if not Void
+ do
+ default_handler := r
+ end
+
+ map (a_id: STRING; h: REQUEST_HANDLER)
+ -- Map handler `h' with `a_id'
+ deferred
+ end
+
+ map_agent (a_id: STRING; a_action: like {REQUEST_AGENT_HANDLER}.action)
+ local
+ h: REQUEST_AGENT_HANDLER
+ do
+ create h.make (a_action)
+ map (a_id, h)
+ end
+
+feature -- Execution
+
+ dispatch (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM): detachable REQUEST_HANDLER
+ -- Dispatch `req, res' to the associated handler
+ -- And return this handler
+ -- If Result is Void, this means no handler was found.
+ local
+ d: like handler
+ ctx: detachable REQUEST_HANDLER_CONTEXT
+ do
+ d := handler (req)
+ if d /= Void then
+ Result := d.handler
+ ctx := d.context
+ else
+ Result := default_handler
+ end
+ if Result /= Void then
+ if ctx = Void then
+ check is_default_handler: Result = default_handler end
+ create {REQUEST_URI_HANDLER_CONTEXT} ctx.make (req, "/")
+ end
+ Result.execute (ctx, req, res)
+ end
+ ensure
+ result_void_implie_no_default: Result = Void implies default_handler = Void
+ end
+
+feature {NONE} -- Access: Implementation
+
+ handler (req: EWSGI_REQUEST): detachable TUPLE [handler: REQUEST_HANDLER; context: REQUEST_HANDLER_CONTEXT]
+ -- Handler whose map matched with `req'
+ require
+ req_valid: req /= Void and then req.environment.path_info /= Void
+ deferred
+ ensure
+ req_path_info_unchanged: req.environment.path_info.same_string (old req.environment.path_info)
+ end
+
+feature {NONE} -- Implementation
+
+ default_handler: detachable REQUEST_HANDLER
+ -- Default handler
+
+;note
+ copyright: "2011-2011, 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/request/router/src/router/request_uri_router.e b/library/server/request/router/src/router/request_uri_router.e
new file mode 100644
index 00000000..b4080739
--- /dev/null
+++ b/library/server/request/router/src/router/request_uri_router.e
@@ -0,0 +1,169 @@
+note
+ description: "Summary description for {REQUEST_URI_ROUTER}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ REQUEST_URI_ROUTER
+
+inherit
+ REQUEST_ROUTER
+
+ ITERABLE [REQUEST_HANDLER]
+ redefine
+ new_cursor
+ end
+
+create
+ make
+
+feature -- Initialization
+
+ make (n: INTEGER)
+ do
+ create handlers.make (n)
+ handlers.compare_objects
+ end
+
+feature -- Registration
+
+ map (p: STRING; h: REQUEST_HANDLER)
+ do
+ handlers.force (h, p)
+ end
+
+feature {NONE} -- Access: Implementation
+
+ handler (req: EWSGI_REQUEST): detachable TUPLE [handler: REQUEST_HANDLER; context: REQUEST_HANDLER_CONTEXT]
+ local
+ h: detachable REQUEST_HANDLER
+ ctx: detachable REQUEST_HANDLER_CONTEXT
+ do
+ h := handler_by_path (req.environment.path_info)
+ if h = Void then
+ if attached smart_handler_by_path (req.environment.path_info) 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
+ Result := [h, ctx]
+ else
+ Result := Void
+ end
+ end
+ end
+
+ smart_handler (req: EWSGI_REQUEST): detachable TUPLE [path: STRING; handler: REQUEST_HANDLER]
+ require
+ req_valid: req /= Void and then req.environment.path_info /= Void
+ do
+ Result := smart_handler_by_path (req.environment.path_info)
+ ensure
+ req_path_info_unchanged: req.environment.path_info.same_string (old req.environment.path_info)
+ end
+
+ handler_by_path (a_path: STRING): detachable REQUEST_HANDLER
+ require
+ a_path_valid: a_path /= Void
+ do
+ Result := handlers.item (context_path (a_path))
+ ensure
+ a_path_unchanged: a_path.same_string (old a_path)
+ end
+
+ smart_handler_by_path (a_path: STRING): detachable TUPLE [path: STRING; handler: REQUEST_HANDLER]
+ require
+ a_path_valid: a_path /= Void
+ local
+ p: INTEGER
+ l_context_path, l_path: STRING
+ h: detachable REQUEST_HANDLER
+ 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)
+ 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 -- Context factory
+
+ handler_context (p: detachable STRING; req: EWSGI_REQUEST): REQUEST_URI_HANDLER_CONTEXT
+ do
+ if p /= Void then
+ create Result.make (req, p)
+ else
+ create Result.make (req, req.environment.path_info)
+ end
+ end
+
+feature -- Access
+
+ new_cursor: HASH_TABLE_ITERATION_CURSOR [REQUEST_HANDLER, STRING]
+ -- Fresh cursor associated with current structure
+ do
+ Result := handlers.new_cursor
+ end
+
+ item (a_path: STRING): detachable REQUEST_HANDLER
+ do
+ Result := handler_by_path (a_path)
+ end
+
+feature {NONE} -- Implementation
+
+ handlers: HASH_TABLE [REQUEST_HANDLER, STRING]
+ -- Handlers
+
+ 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-2011, 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/request/router/src/router/request_uri_template_router.e b/library/server/request/router/src/router/request_uri_template_router.e
new file mode 100644
index 00000000..d5a43d33
--- /dev/null
+++ b/library/server/request/router/src/router/request_uri_template_router.e
@@ -0,0 +1,138 @@
+note
+ description: "Summary description for {REQUEST_URI_TEMPLATE_ROUTER}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ REQUEST_URI_TEMPLATE_ROUTER
+
+inherit
+ REQUEST_ROUTER
+
+ ITERABLE [REQUEST_HANDLER]
+ redefine
+ new_cursor
+ end
+
+create
+ make
+
+feature -- Initialization
+
+ make (n: INTEGER)
+ do
+ create handlers.make (n)
+ create templates.make (n)
+ handlers.compare_objects
+ end
+
+feature -- Registration
+
+ map_with_uri_template (uri: URI_TEMPLATE; h: REQUEST_HANDLER)
+ do
+ handlers.force (h, uri.template)
+ templates.force (uri, uri.template)
+ end
+
+ map (tpl: STRING; h: REQUEST_HANDLER)
+ local
+ uri: URI_TEMPLATE
+ do
+ create uri.make (tpl)
+ map_with_uri_template (uri, h)
+ end
+
+feature {NONE} -- Access: Implementation
+
+ handler (req: EWSGI_REQUEST): detachable TUPLE [handler: REQUEST_HANDLER; context: REQUEST_HANDLER_CONTEXT]
+ local
+ ctx: detachable REQUEST_URI_TEMPLATE_HANDLER_CONTEXT
+ l_handlers: like handlers
+ t: STRING
+ p: STRING
+ do
+ p := req.environment.request_uri
+ from
+ l_handlers := handlers
+ l_handlers.start
+ until
+ l_handlers.after or Result /= Void
+ loop
+ t := l_handlers.key_for_iteration
+ if attached templates.item (t) as tpl and then
+ attached tpl.match (p) as res
+ then
+ ctx := handler_context (p, req, tpl, res)
+ Result := [l_handlers.item_for_iteration, ctx]
+ end
+ l_handlers.forth
+ end
+ end
+
+feature -- Context factory
+
+ handler_context (p: detachable STRING; req: EWSGI_REQUEST; tpl: URI_TEMPLATE; tpl_res: URI_TEMPLATE_MATCH_RESULT): REQUEST_URI_TEMPLATE_HANDLER_CONTEXT
+ do
+ if p /= Void then
+ create Result.make (req, tpl, tpl_res, p)
+ else
+ create Result.make (req, tpl, tpl_res, req.environment.path_info)
+ end
+ end
+
+feature -- Access
+
+ new_cursor: HASH_TABLE_ITERATION_CURSOR [REQUEST_HANDLER, STRING]
+ -- Fresh cursor associated with current structure
+ do
+ Result := handlers.new_cursor
+ end
+
+ item (a_path: STRING): detachable REQUEST_HANDLER
+ do
+ Result := handlers.item (a_path)
+ end
+
+feature {NONE} -- Implementation
+
+ handlers: HASH_TABLE [REQUEST_HANDLER, STRING]
+ -- Handlers indexed by the template expression
+ -- see `templates'
+
+ templates: HASH_TABLE [URI_TEMPLATE, STRING]
+ -- 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-2011, 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