Added first draft for a URI and/or URI-template base request router.
This commit is contained in:
14
library/server/request/router/doc/README.txt
Normal file
14
library/server/request/router/doc/README.txt
Normal file
@@ -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)
|
||||||
10
library/server/request/router/license.lic
Normal file
10
library/server/request/router/license.lic
Normal file
@@ -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
|
||||||
|
]"
|
||||||
20
library/server/request/router/router-safe.ecf
Normal file
20
library/server/request/router/router-safe.ecf
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="router" uuid="7E530655-8578-4AF8-99CA-175A0025D843" library_target="router">
|
||||||
|
<target name="router">
|
||||||
|
<root all_classes="true"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.git$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||||
|
<assertions precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="ewsgi_spec" location="..\..\ewsgi\ewsgi_specification-safe.ecf" readonly="false"/>
|
||||||
|
<library name="http" location="..\..\..\protocol\http\http-safe.ecf"/>
|
||||||
|
<library name="uri_template" location="..\..\..\protocol\uri_template\uri_template-safe.ecf" readonly="false"/>
|
||||||
|
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||||
|
<cluster name="src" location="src\" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
20
library/server/request/router/router.ecf
Normal file
20
library/server/request/router/router.ecf
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="router" uuid="7E530655-8578-4AF8-99CA-175A0025D843" library_target="router">
|
||||||
|
<target name="router">
|
||||||
|
<root all_classes="true"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.git$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||||
|
<assertions precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="ewsgi_spec" location="..\..\ewsgi\ewsgi_specification-safe.ecf" readonly="false"/>
|
||||||
|
<library name="http" location="..\..\..\protocol\http\http-safe.ecf"/>
|
||||||
|
<library name="uri_template" location="..\..\..\protocol\uri_template\uri_template-safe.ecf" readonly="false"/>
|
||||||
|
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||||
|
<cluster name="src" location="src\" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
310
library/server/request/router/src/handler/request_handler.e
Normal file
310
library/server/request/router/src/handler/request_handler.e
Normal file
@@ -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
|
||||||
87
library/server/request/router/src/router/request_router.e
Normal file
87
library/server/request/router/src/router/request_router.e
Normal file
@@ -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
|
||||||
169
library/server/request/router/src/router/request_uri_router.e
Normal file
169
library/server/request/router/src/router/request_uri_router.e
Normal file
@@ -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
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user