make router more easy to inherit from and specialized

This commit is contained in:
Jocelyn Fiat
2011-09-09 14:10:54 +02:00
parent fb8412fcae
commit 11286eeeef
9 changed files with 164 additions and 175 deletions

View File

@@ -140,7 +140,7 @@ feature -- Execution
msg := "Hello anonymous visitor !%N"
end
content_type_supported := <<{HTTP_CONSTANTS}.json_app, {HTTP_CONSTANTS}.html_text, {HTTP_CONSTANTS}.xml_text, {HTTP_CONSTANTS}.plain_text>>
inspect request_format_id (ctx, "format", content_type_supported)
inspect ctx.request_format_id ("format", content_type_supported)
when {HTTP_FORMAT_CONSTANTS}.json then
l_response_content_type := {HTTP_CONSTANTS}.json_app
msg := "{%N%"application%": %"/hello%",%N %"message%": %"" + msg + "%" %N}"

View File

@@ -7,15 +7,15 @@
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
<assertions precondition="true"/>
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi-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"/>
<library name="uri_template" location="..\..\..\protocol\uri_template\uri_template-safe.ecf" readonly="false"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
<target name="ewsgi_compliant_router">
@@ -32,8 +32,8 @@
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi_spec-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"/>
<library name="uri_template" location="..\..\..\protocol\uri_template\uri_template-safe.ecf" readonly="false"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</target>
</system>

View File

@@ -36,10 +36,11 @@ feature -- Setup
feature -- Execution
execute (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
local
l_handled: BOOLEAN
do
if attached router.dispatch (req, res) as r then
--| done
else
l_handled := router.dispatch (req, res)
if not l_handled then
execute_default (req, res)
end
end

View File

@@ -15,14 +15,59 @@ inherit
{NONE} all
end
HTTP_FORMAT_CONSTANTS
export
{NONE} all
end
feature -- Access
request: WGI_REQUEST
-- Associated request
path: READABLE_STRING_GENERAL
path: READABLE_STRING_8
-- Associated path
request_format (a_format_variable_name: detachable STRING; content_type_supported: detachable ARRAY [STRING]): detachable READABLE_STRING_8
-- Format id for the request based on {HTTP_FORMAT_CONSTANTS}
local
do
if a_format_variable_name /= Void and then attached parameter (a_format_variable_name) as ctx_format then
Result := ctx_format.as_string_8
else
Result := content_type_to_request_format (request_content_type (content_type_supported))
end
end
request_format_id (a_format_variable_name: detachable STRING; content_type_supported: detachable ARRAY [STRING]): INTEGER
-- Format id for the request based on {HTTP_FORMAT_CONSTANTS}
do
if attached request_format (a_format_variable_name, content_type_supported) as l_format then
Result := format_id (l_format)
else
Result := 0
end
end
content_type_to_request_format (a_content_type: detachable READABLE_STRING_8): detachable READABLE_STRING_8
-- `a_content_type' converted into a request format name
do
if a_content_type /= Void then
if a_content_type.same_string ({HTTP_CONSTANTS}.json_text) then
Result := {HTTP_FORMAT_CONSTANTS}.json_name
elseif a_content_type.same_string ({HTTP_CONSTANTS}.json_app) then
Result := {HTTP_FORMAT_CONSTANTS}.json_name
elseif a_content_type.same_string ({HTTP_CONSTANTS}.xml_text) then
Result := {HTTP_FORMAT_CONSTANTS}.xml_name
elseif a_content_type.same_string ({HTTP_CONSTANTS}.html_text) then
Result := {HTTP_FORMAT_CONSTANTS}.html_name
elseif a_content_type.same_string ({HTTP_CONSTANTS}.plain_text) then
Result := {HTTP_FORMAT_CONSTANTS}.text_name
end
end
end
request_content_type (content_type_supported: detachable ARRAY [STRING]): detachable READABLE_STRING_8
local
s: detachable READABLE_STRING_32

View File

@@ -7,6 +7,14 @@ note
deferred class
REQUEST_HANDLER
inherit
ANY
ROUTED_APPLICATION_HELPER
export
{NONE} all
end
feature {NONE} -- Initialization
initialize
@@ -42,7 +50,7 @@ feature -- Execution
execute_application (a_hdl_context, req, res)
post_execute (req, res)
else
execute_method_not_allowed (a_hdl_context, req, res)
execute_request_method_not_allowed (req, res, supported_request_method_names)
end
else
rescue_execute (req, res)
@@ -52,29 +60,6 @@ feature -- Execution
retry
end
execute_method_not_allowed (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
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: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- Execute request handler
deferred
@@ -101,76 +86,27 @@ feature -- Execution
feature -- Execution: report
-- execution_information (req: WGI_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: WGI_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: WGI_REQUEST; args: detachable STRING; abs: BOOLEAN): STRING
-- Associated url based on `path' and `args'
url (req: WGI_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 := req.request_uri + "/" + s
s := l_base + "/" + s
else
s := req.request_uri + s
s := l_base + s
end
else
s := req.request_uri
s := l_base
end
if abs then
Result := req.absolute_script_url (s)

View File

@@ -10,11 +10,6 @@ class
inherit
ANY
HTTP_FORMAT_CONSTANTS
export
{NONE} all
end
feature -- Helper
execute_content_type_not_allowed (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER; a_content_types: detachable ARRAY [STRING]; a_uri_formats: detachable ARRAY [STRING])
@@ -70,26 +65,22 @@ feature -- Helper
end
end
execute_method_not_allowed (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER; a_methods: ARRAY [STRING])
execute_request_method_not_allowed (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER; a_methods: ITERABLE [STRING])
local
s: STRING
i, n: INTEGER
do
create s.make (10)
from
i := a_methods.lower
n := a_methods.upper
until
i > n
res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed)
create s.make (25)
across
a_methods as c
loop
s.append_string (a_methods[i])
if i < n then
if not s.is_empty then
s.append_character (',')
s.append_character (' ')
end
i := i + 1
s.append_string (c.item)
end
res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed)
res.write_header ({HTTP_STATUS_CODE}.method_not_allowed, <<
["Content-Type", {HTTP_CONSTANTS}.plain_text],
["Allow", s]
@@ -97,43 +88,6 @@ feature -- Helper
res.write_string ("Unsupported request method, Allow: " + s + "%N")
end
feature -- Context helper
request_format_id (ctx: REQUEST_HANDLER_CONTEXT; a_format_variable_name: detachable STRING; content_type_supported: detachable ARRAY [STRING]): INTEGER
-- Format id for the request based on {HTTP_FORMAT_CONSTANTS}
local
l_format: detachable STRING_8
do
if a_format_variable_name /= Void and then attached ctx.parameter (a_format_variable_name) as ctx_format then
l_format := ctx_format.as_string_8
else
l_format := content_type_to_request_format (ctx.request_content_type (content_type_supported))
end
if l_format /= Void then
Result := format_id (l_format)
else
Result := 0
end
end
content_type_to_request_format (a_content_type: detachable READABLE_STRING_8): detachable STRING
-- `a_content_type' converted into a request format name
do
if a_content_type /= Void then
if a_content_type.same_string ({HTTP_CONSTANTS}.json_text) then
Result := {HTTP_FORMAT_CONSTANTS}.json_name
elseif a_content_type.same_string ({HTTP_CONSTANTS}.json_app) then
Result := {HTTP_FORMAT_CONSTANTS}.json_name
elseif a_content_type.same_string ({HTTP_CONSTANTS}.xml_text) then
Result := {HTTP_FORMAT_CONSTANTS}.xml_name
elseif a_content_type.same_string ({HTTP_CONSTANTS}.html_text) then
Result := {HTTP_FORMAT_CONSTANTS}.html_name
elseif a_content_type.same_string ({HTTP_CONSTANTS}.plain_text) then
Result := {HTTP_FORMAT_CONSTANTS}.text_name
end
end
end
note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -7,9 +7,6 @@ note
deferred class
REQUEST_ROUTER
inherit
ITERABLE [TUPLE [handler: REQUEST_HANDLER; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]]
feature -- Registration
map_default (r: like default_handler)
@@ -17,7 +14,7 @@ feature -- Registration
-- If no route/handler is found,
-- then use `default_handler' as default if not Void
do
default_handler := r
set_default_handler (r)
end
map (a_id: READABLE_STRING_8; h: REQUEST_HANDLER)
@@ -46,13 +43,20 @@ feature -- Registration
feature -- Execution
dispatch (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER): detachable REQUEST_HANDLER
dispatch (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER): BOOLEAN
-- Dispatch `req, res' to the associated handler
-- And return True is handled, otherwise False
do
Result := dispatch_and_return_handler (req, res) /= Void
end
dispatch_and_return_handler (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER): like default_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
ctx: detachable like default_handler_context
do
d := handler (req)
if d /= Void then
@@ -60,21 +64,29 @@ feature -- Execution
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, "/")
if Result /= Void then
ctx := default_handler_context (req)
end
end
if Result /= Void and ctx /= Void then
Result.execute (ctx, req, res)
end
ensure
result_void_implie_no_default: Result = Void implies default_handler = Void
end
feature -- Traversing
new_cursor: ITERATION_CURSOR [TUPLE [handler: REQUEST_HANDLER; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]]
-- Fresh cursor associated with current structure
deferred
ensure
result_attached: Result /= Void
end
feature {NONE} -- Access: Implementation
handler (req: WGI_REQUEST): detachable TUPLE [handler: REQUEST_HANDLER; context: REQUEST_HANDLER_CONTEXT]
handler (req: WGI_REQUEST): detachable TUPLE [handler: attached like default_handler; context: like default_handler_context]
-- Handler whose map matched with `req'
require
req_valid: req /= Void and then req.path_info /= Void
@@ -127,8 +139,24 @@ feature {NONE} -- Access: Implementation
feature {NONE} -- Implementation
set_default_handler (h: like default_handler)
-- Set `default_handler' to `h'
deferred
ensure
default_handler_set: h = default_handler
end
default_handler: detachable REQUEST_HANDLER
-- Default handler
deferred
end
default_handler_context (req: WGI_REQUEST): REQUEST_HANDLER_CONTEXT
-- Default handler context associated with `default_handler'
require
has_default_handler: default_handler /= Void
deferred
end
;note
copyright: "2011-2011, Eiffel Software and others"

View File

@@ -30,10 +30,10 @@ feature -- Registration
feature {NONE} -- Access: Implementation
handler (req: WGI_REQUEST): detachable TUPLE [handler: REQUEST_HANDLER; context: REQUEST_HANDLER_CONTEXT]
handler (req: WGI_REQUEST): detachable TUPLE [handler: REQUEST_HANDLER; context: like default_handler_context]
local
h: detachable REQUEST_HANDLER
ctx: detachable REQUEST_HANDLER_CONTEXT
ctx: detachable like default_handler_context
do
h := handler_by_path (req.path_info, req.request_method)
if h = Void then
@@ -82,7 +82,6 @@ feature {NONE} -- Access: Implementation
end
l_handlers.forth
end
-- Result := handlers.item (context_path (a_path))
ensure
a_path_unchanged: a_path.same_string (old a_path)
end
@@ -115,14 +114,14 @@ feature {NONE} -- Access: Implementation
a_path_unchanged: a_path.same_string (old a_path)
end
feature -- Context factory
feature {NONE} -- Context factory
handler_context (p: detachable STRING; req: WGI_REQUEST): REQUEST_URI_HANDLER_CONTEXT
handler_context (p: detachable STRING; req: WGI_REQUEST): like default_handler_context
do
if p /= Void then
create Result.make (req, p)
create {REQUEST_URI_HANDLER_CONTEXT} Result.make (req, p)
else
create Result.make (req, req.path_info)
create {REQUEST_URI_HANDLER_CONTEXT} Result.make (req, req.path_info)
end
end
@@ -165,7 +164,21 @@ feature {NONE} -- Implementation
result_not_empty: not Result.is_empty
end
;note
feature {NONE} -- Default: implementation
default_handler: detachable REQUEST_HANDLER
set_default_handler (h: like default_handler)
do
default_handler := h
end
default_handler_context (req: WGI_REQUEST): REQUEST_HANDLER_CONTEXT
do
Result := handler_context (Void, req)
end
note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[

View File

@@ -45,9 +45,8 @@ feature -- Registration
feature {NONE} -- Access: Implementation
handler (req: WGI_REQUEST): detachable TUPLE [handler: REQUEST_HANDLER; context: REQUEST_HANDLER_CONTEXT]
handler (req: WGI_REQUEST): detachable TUPLE [handler: attached like default_handler; context: like default_handler_context]
local
ctx: detachable REQUEST_URI_TEMPLATE_HANDLER_CONTEXT
l_handlers: like handlers
t: STRING
p: STRING
@@ -67,8 +66,7 @@ feature {NONE} -- Access: Implementation
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_info.handler, ctx]
Result := [l_info.handler, handler_context (p, req, tpl, res)]
end
end
end
@@ -76,14 +74,14 @@ feature {NONE} -- Access: Implementation
end
end
feature -- Context factory
feature {NONE} -- Context factory
handler_context (p: detachable STRING; req: WGI_REQUEST; tpl: URI_TEMPLATE; tpl_res: URI_TEMPLATE_MATCH_RESULT): REQUEST_URI_TEMPLATE_HANDLER_CONTEXT
handler_context (p: detachable STRING; req: WGI_REQUEST; tpl: URI_TEMPLATE; tpl_res: URI_TEMPLATE_MATCH_RESULT): like default_handler_context
do
if p /= Void then
create Result.make (req, tpl, tpl_res, p)
create {REQUEST_URI_TEMPLATE_HANDLER_CONTEXT} Result.make (req, tpl, tpl_res, p)
else
create Result.make (req, tpl, tpl_res, req.path_info)
create {REQUEST_URI_TEMPLATE_HANDLER_CONTEXT} Result.make (req, tpl, tpl_res, req.path_info)
end
end
@@ -126,6 +124,20 @@ feature {NONE} -- Implementation
result_not_empty: not Result.is_empty
end
feature {NONE} -- Default: implementation
default_handler: detachable REQUEST_HANDLER
set_default_handler (h: like default_handler)
do
default_handler := h
end
default_handler_context (req: WGI_REQUEST): REQUEST_HANDLER_CONTEXT
do
create {REQUEST_URI_HANDLER_CONTEXT} Result.make (req, "/")
end
;note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"