Now the 'router' library is part of 'wsf'
Move hello_routed_world under tests/dev since it was not really an example, but more a dev workspace/test
This commit is contained in:
@@ -6,6 +6,11 @@ It is built on top of [EWSGI](../ewsgi/) to benefit from the various EWSGI conne
|
||||
## Requirements
|
||||
* [EWSGI](../ewsgi)
|
||||
* [HTTP](../../protocol/http)
|
||||
* [URI template](../../protocol/uri_template)
|
||||
|
||||
## Content
|
||||
* Core classes for the Web Server Framework
|
||||
* [Router](router) library
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
14
library/server/wsf/doc/router.txt
Normal file
14
library/server/wsf/doc/router.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
This library introduce the notion of router and handler in the folder "router"
|
||||
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)
|
||||
9
library/server/wsf/router/README.md
Normal file
9
library/server/wsf/router/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Router
|
||||
|
||||
## Requirements
|
||||
* [URI Template](../../../protocol/rui_template)
|
||||
|
||||
## Overview
|
||||
|
||||
## Usage
|
||||
|
||||
23
library/server/wsf/router/default/routed_service.e
Normal file
23
library/server/wsf/router/default/routed_service.e
Normal file
@@ -0,0 +1,23 @@
|
||||
note
|
||||
description: "Summary description for {DEFAULT_ROUTED_SERVICE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
ROUTED_SERVICE
|
||||
|
||||
inherit
|
||||
ROUTED_SERVICE_I [REQUEST_HANDLER [REQUEST_HANDLER_CONTEXT], REQUEST_HANDLER_CONTEXT]
|
||||
|
||||
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/wsf/router/misc/request_format_utility.e
Normal file
87
library/server/wsf/router/misc/request_format_utility.e
Normal file
@@ -0,0 +1,87 @@
|
||||
note
|
||||
description: "Summary description for {REQUEST_FORMAT_UTILITY}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
REQUEST_FORMAT_UTILITY
|
||||
|
||||
feature -- Access
|
||||
|
||||
accepted_content_types (req: WSF_REQUEST): detachable ARRAYED_LIST [READABLE_STRING_8]
|
||||
local
|
||||
s: STRING_8
|
||||
q: READABLE_STRING_8
|
||||
p: INTEGER
|
||||
lst: LIST [READABLE_STRING_8]
|
||||
qs: QUICK_SORTER [READABLE_STRING_8]
|
||||
do
|
||||
--TEST if attached ("text/html,application/xhtml+xml;q=0.6,application/xml;q=0.2,text/plain;q=0.5,*/*;q=0.8") as l_accept then
|
||||
if attached req.http_accept as l_accept then
|
||||
lst := l_accept.as_string_8.split (',')
|
||||
create Result.make (lst.count)
|
||||
from
|
||||
lst.start
|
||||
until
|
||||
lst.after
|
||||
loop
|
||||
s := lst.item
|
||||
p := s.substring_index (";q=", 1)
|
||||
if p > 0 then
|
||||
q := s.substring (p + 3, s.count)
|
||||
s := s.substring (1, p - 1)
|
||||
else
|
||||
q := "1.0"
|
||||
end
|
||||
Result.force (q + ":" + s)
|
||||
|
||||
lst.forth
|
||||
end
|
||||
create qs.make (create {COMPARABLE_COMPARATOR [READABLE_STRING_8]})
|
||||
qs.reverse_sort (Result)
|
||||
from
|
||||
Result.start
|
||||
until
|
||||
Result.after
|
||||
loop
|
||||
s := Result.item
|
||||
p := s.index_of (':', 1)
|
||||
if p > 0 then
|
||||
s.remove_head (p)
|
||||
else
|
||||
check should_have_colon: False end
|
||||
end
|
||||
Result.forth
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
string_in_array (arr: ARRAY [STRING]; s: STRING): BOOLEAN
|
||||
local
|
||||
i,n: INTEGER
|
||||
do
|
||||
from
|
||||
i := arr.lower
|
||||
n := arr.upper
|
||||
until
|
||||
i > n or Result
|
||||
loop
|
||||
Result := s.same_string (arr[i])
|
||||
i := i + 1
|
||||
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,44 @@
|
||||
note
|
||||
description: "[
|
||||
Summary description for REQUEST_HANDLER_ROUTES_RECORDER.
|
||||
|
||||
You can inherit from this class from any REQUEST_HANDLER and redefine `on_handler_mapped'
|
||||
to record the available routes if your handler needs it.
|
||||
]"
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
REQUEST_HANDLER_ROUTES_RECORDER
|
||||
|
||||
feature {REQUEST_HANDLER} -- Routes access
|
||||
|
||||
available_routes: detachable LIST [TUPLE [resource: READABLE_STRING_8; rqst_methods: detachable ARRAY [READABLE_STRING_8]]]
|
||||
-- Available routes
|
||||
|
||||
feature {REQUEST_ROUTER} -- Routes change
|
||||
|
||||
on_handler_mapped (a_resource: READABLE_STRING_8; a_rqst_methods: detachable ARRAY [READABLE_STRING_8])
|
||||
local
|
||||
l_routes: like available_routes
|
||||
do
|
||||
l_routes := available_routes
|
||||
if l_routes = Void then
|
||||
create {ARRAYED_LIST [like available_routes.item]} l_routes.make (3)
|
||||
available_routes := l_routes
|
||||
end
|
||||
l_routes.force ([a_resource, a_rqst_methods])
|
||||
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
|
||||
296
library/server/wsf/router/misc/request_resource_handler_helper.e
Normal file
296
library/server/wsf/router/misc/request_resource_handler_helper.e
Normal file
@@ -0,0 +1,296 @@
|
||||
note
|
||||
description: "Work in progress Common abstraction to handle RESTfull methods"
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
REQUEST_RESOURCE_HANDLER_HELPER [C -> REQUEST_HANDLER_CONTEXT]
|
||||
|
||||
feature -- Execute template
|
||||
|
||||
execute_methods (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request and dispatch according to the request method
|
||||
local
|
||||
m: READABLE_STRING_8
|
||||
do
|
||||
m := req.request_method.as_upper
|
||||
if m.same_string ({HTTP_REQUEST_METHODS}.method_get) then
|
||||
execute_get (ctx, req, res)
|
||||
elseif m.same_string ({HTTP_REQUEST_METHODS}.method_put) then
|
||||
execute_put (ctx, req, res)
|
||||
elseif m.same_string ({HTTP_REQUEST_METHODS}.method_delete) then
|
||||
execute_delete (ctx, req, res)
|
||||
elseif m.same_string ({HTTP_REQUEST_METHODS}.method_post) then
|
||||
execute_post (ctx, req, res)
|
||||
elseif m.same_string ({HTTP_REQUEST_METHODS}.method_trace) then
|
||||
execute_trace (ctx, req, res)
|
||||
elseif m.same_string ({HTTP_REQUEST_METHODS}.method_options) then
|
||||
execute_options (ctx, req, res)
|
||||
elseif m.same_string ({HTTP_REQUEST_METHODS}.method_head) then
|
||||
execute_head (ctx, req, res)
|
||||
elseif m.same_string ({HTTP_REQUEST_METHODS}.method_connect) then
|
||||
execute_connect (ctx, req, res)
|
||||
else
|
||||
--| Eventually handle other methods...
|
||||
execute_extension_method (ctx, req, res)
|
||||
end
|
||||
rescue
|
||||
handle_internal_server_error ("Internal Server Error", ctx, req, res)
|
||||
end
|
||||
|
||||
feature -- Method Post
|
||||
|
||||
execute_post (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
if req.content_length_value > 0 then
|
||||
do_post (ctx, req, res)
|
||||
else
|
||||
handle_bad_request_response ("Bad request, content_length empty", ctx, req, res)
|
||||
end
|
||||
end
|
||||
|
||||
do_post (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
handle_not_implemented ("Method POST not implemented", ctx, req, res)
|
||||
end
|
||||
|
||||
feature-- Method Put
|
||||
|
||||
execute_put (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
if req.content_length_value > 0 then
|
||||
do_put (ctx, req, res)
|
||||
else
|
||||
handle_bad_request_response ("Bad request, content_length empty", ctx, req, res)
|
||||
end
|
||||
end
|
||||
|
||||
do_put (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
handle_not_implemented ("Method PUT not implemented", ctx, req, res)
|
||||
end
|
||||
|
||||
feature -- Method Get
|
||||
|
||||
execute_get (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
do_get (ctx, req, res)
|
||||
end
|
||||
|
||||
do_get (ctx: C;req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
handle_not_implemented ("Method HEAD not implemented", ctx, req, res)
|
||||
end
|
||||
|
||||
feature -- Method DELETE
|
||||
|
||||
execute_delete (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
do_delete (ctx, req, res)
|
||||
end
|
||||
|
||||
do_delete (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
handle_not_implemented ("Method DELETE not implemented", ctx, req, res)
|
||||
end
|
||||
|
||||
feature -- Method CONNECT
|
||||
|
||||
execute_connect (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
do_connect (ctx, req, res)
|
||||
end
|
||||
|
||||
do_connect (ctx: C;req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
handle_not_implemented ("Method CONNECT not implemented", ctx, req, res)
|
||||
end
|
||||
|
||||
feature -- Method HEAD
|
||||
|
||||
execute_head (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
do_head (ctx, req, res)
|
||||
end
|
||||
|
||||
do_head (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
handle_not_implemented ("Method HEAD not implemented", ctx, req, res)
|
||||
end
|
||||
|
||||
feature -- Method OPTIONS
|
||||
|
||||
execute_options (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
do_options (ctx, req, res)
|
||||
end
|
||||
|
||||
do_options (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
handle_not_implemented ("Method OPTIONS not implemented", ctx, req, res)
|
||||
end
|
||||
|
||||
feature -- Method TRACE
|
||||
|
||||
execute_trace (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
do_trace (ctx, req, res)
|
||||
end
|
||||
|
||||
do_trace (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
handle_not_implemented ("Method TRACE not implemented", ctx, req, res)
|
||||
end
|
||||
|
||||
feature -- Method Extension Method
|
||||
|
||||
execute_extension_method (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
do_extension_method (ctx, req, res)
|
||||
end
|
||||
|
||||
do_extension_method (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
handle_not_implemented ("Method extension-method not implemented", ctx, req, res)
|
||||
end
|
||||
|
||||
feature -- Handle responses
|
||||
-- TODO Handle Content negotiation.
|
||||
-- The option : Server-driven negotiation: uses request headers to select a variant
|
||||
-- More info : http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12
|
||||
|
||||
supported_content_types: detachable ARRAY [READABLE_STRING_8]
|
||||
-- Supported content types
|
||||
-- Can be redefined
|
||||
do
|
||||
Result := Void
|
||||
end
|
||||
|
||||
handle_bad_request_response (a_description: STRING; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE )
|
||||
local
|
||||
h : HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_status ({HTTP_STATUS_CODE}.bad_request)
|
||||
h.put_content_type_application_json
|
||||
h.put_content_length (a_description.count)
|
||||
h.put_current_date
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.bad_request)
|
||||
res.write_header_text (h.string)
|
||||
res.write_string (a_description)
|
||||
end
|
||||
|
||||
|
||||
handle_precondition_fail_response (a_description: STRING; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE )
|
||||
local
|
||||
h : HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_status ({HTTP_STATUS_CODE}.precondition_failed)
|
||||
h.put_content_type_application_json
|
||||
h.put_content_length (a_description.count)
|
||||
h.put_current_date
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.precondition_failed)
|
||||
res.write_header_text (h.string)
|
||||
res.write_string (a_description)
|
||||
end
|
||||
|
||||
handle_internal_server_error (a_description: STRING; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE )
|
||||
local
|
||||
h : HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_status ({HTTP_STATUS_CODE}.internal_server_error)
|
||||
h.put_content_type_application_json
|
||||
h.put_content_length (a_description.count)
|
||||
h.put_current_date
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error)
|
||||
res.write_header_text (h.string)
|
||||
res.write_string (a_description)
|
||||
end
|
||||
|
||||
handle_not_implemented (a_description: STRING; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE )
|
||||
local
|
||||
h : HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_status ({HTTP_STATUS_CODE}.not_implemented)
|
||||
h.put_content_type_application_json
|
||||
h.put_content_length (a_description.count)
|
||||
h.put_current_date
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.not_implemented)
|
||||
res.write_header_text (h.string)
|
||||
res.write_string (a_description)
|
||||
end
|
||||
|
||||
handle_method_not_allowed_response (a_description: STRING; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
h : HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_status ({HTTP_STATUS_CODE}.method_not_allowed)
|
||||
h.put_content_type_application_json
|
||||
h.put_content_length (a_description.count)
|
||||
h.put_current_date
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed)
|
||||
res.write_header_text (h.string)
|
||||
res.write_string (a_description)
|
||||
end
|
||||
|
||||
handle_resource_not_found_response (a_description: STRING; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
h : HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_status ({HTTP_STATUS_CODE}.not_found)
|
||||
h.put_content_type_application_json
|
||||
h.put_content_length (a_description.count)
|
||||
h.put_current_date
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.not_found)
|
||||
res.write_header_text (h.string)
|
||||
res.write_string (a_description)
|
||||
end
|
||||
|
||||
|
||||
handle_resource_not_modified_response (a_description: STRING; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
h : HTTP_HEADER
|
||||
do
|
||||
res.flush
|
||||
create h.make
|
||||
h.put_status ({HTTP_STATUS_CODE}.not_modified)
|
||||
h.put_content_type_application_json
|
||||
h.put_content_length (a_description.count)
|
||||
h.put_current_date
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.not_modified)
|
||||
res.write_header_text (h.string)
|
||||
res.write_string (a_description)
|
||||
end
|
||||
|
||||
|
||||
handle_resource_conflict_response (a_description: STRING; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
h : HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_status ({HTTP_STATUS_CODE}.conflict)
|
||||
h.put_content_type_application_json
|
||||
h.put_content_length (a_description.count)
|
||||
h.put_current_date
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.conflict)
|
||||
res.write_header_text (h.string)
|
||||
res.write_string (a_description)
|
||||
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
|
||||
98
library/server/wsf/router/misc/routed_service_helper.e
Normal file
98
library/server/wsf/router/misc/routed_service_helper.e
Normal file
@@ -0,0 +1,98 @@
|
||||
note
|
||||
description: "Summary description for {ROUTED_SERVICE_HELPER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ROUTED_SERVICE_HELPER
|
||||
|
||||
inherit
|
||||
ANY
|
||||
|
||||
feature -- Helper
|
||||
|
||||
execute_content_type_not_allowed (req: WSF_REQUEST; res: WSF_RESPONSE; a_content_types: detachable ARRAY [STRING]; a_uri_formats: detachable ARRAY [STRING])
|
||||
local
|
||||
accept_s, uri_s: detachable STRING
|
||||
i, n: INTEGER
|
||||
do
|
||||
if a_content_types /= Void then
|
||||
create accept_s.make (10)
|
||||
from
|
||||
i := a_content_types.lower
|
||||
n := a_content_types.upper
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
accept_s.append_string (a_content_types[i])
|
||||
if i < n then
|
||||
accept_s.append_character (',')
|
||||
accept_s.append_character (' ')
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
accept_s := "*/*"
|
||||
end
|
||||
|
||||
if a_uri_formats /= Void then
|
||||
create uri_s.make (10)
|
||||
from
|
||||
i := a_uri_formats.lower
|
||||
n := a_uri_formats.upper
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
uri_s.append_string (a_uri_formats[i])
|
||||
if i < n then
|
||||
uri_s.append_character (',')
|
||||
uri_s.append_character (' ')
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.unsupported_media_type)
|
||||
res.write_header ({HTTP_STATUS_CODE}.unsupported_media_type, << ["Content-Type", "text/plain"], ["Accept", accept_s]>>)
|
||||
if accept_s /= Void then
|
||||
res.write_string ("Unsupported request content-type, Accept: " + accept_s + "%N")
|
||||
end
|
||||
if uri_s /= Void then
|
||||
res.write_string ("Unsupported request format from the URI: " + uri_s + "%N")
|
||||
end
|
||||
end
|
||||
|
||||
execute_request_method_not_allowed (req: WSF_REQUEST; res: WSF_RESPONSE; a_methods: ITERABLE [STRING])
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed)
|
||||
create s.make (25)
|
||||
across
|
||||
a_methods as c
|
||||
loop
|
||||
if not s.is_empty then
|
||||
s.append_character (',')
|
||||
s.append_character (' ')
|
||||
end
|
||||
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_MIME_TYPES}.text_plain],
|
||||
["Allow", s]
|
||||
>>)
|
||||
res.write_string ("Unsupported request method, Allow: " + s + "%N")
|
||||
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
|
||||
44
library/server/wsf/router/request_agent_handler.e
Normal file
44
library/server/wsf/router/request_agent_handler.e
Normal file
@@ -0,0 +1,44 @@
|
||||
note
|
||||
description: "Summary description for REQUEST_AGENT_HANDLER."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
REQUEST_AGENT_HANDLER [C -> REQUEST_HANDLER_CONTEXT]
|
||||
|
||||
inherit
|
||||
REQUEST_HANDLER [C]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (act: like action)
|
||||
do
|
||||
action := act
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
action: PROCEDURE [ANY, TUPLE [ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE]]
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
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
|
||||
324
library/server/wsf/router/request_file_system_handler.e
Normal file
324
library/server/wsf/router/request_file_system_handler.e
Normal file
@@ -0,0 +1,324 @@
|
||||
note
|
||||
description: "[
|
||||
Request handler used to respond file system request.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
REQUEST_FILE_SYSTEM_HANDLER [C -> REQUEST_HANDLER_CONTEXT]
|
||||
|
||||
inherit
|
||||
REQUEST_HANDLER [C]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_root: READABLE_STRING_8)
|
||||
require
|
||||
a_root_exists: node_exists (a_root)
|
||||
do
|
||||
document_root := a_root
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
document_root: READABLE_STRING_8
|
||||
-- Document root for the file system
|
||||
|
||||
directory_index: detachable ARRAY [READABLE_STRING_8]
|
||||
-- File serve if a directory index is requested
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_directory_index (idx: like directory_index)
|
||||
-- Set `directory_index' as `idx'
|
||||
do
|
||||
if idx = Void or else idx.is_empty then
|
||||
directory_index := Void
|
||||
else
|
||||
directory_index := idx
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
s: STRING
|
||||
uri: STRING
|
||||
do
|
||||
if attached {WSF_STRING} ctx.path_parameter ("path") as l_path then
|
||||
uri := l_path.string
|
||||
process_uri (uri, ctx, req, res)
|
||||
else
|
||||
create h.make
|
||||
h.put_content_type_text_html
|
||||
s := "Hello " + ctx.path + "%N"
|
||||
s.append ("root=" + document_root)
|
||||
|
||||
h.put_content_length (s.count)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
res.write_header_text (h.string)
|
||||
res.write_string (s)
|
||||
end
|
||||
end
|
||||
|
||||
process_uri (uri: READABLE_STRING_8; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
f: RAW_FILE
|
||||
fn: READABLE_STRING_8
|
||||
do
|
||||
fn := resource_filename (uri)
|
||||
create f.make (fn)
|
||||
if f.exists then
|
||||
if f.is_readable then
|
||||
if f.is_directory then
|
||||
respond_index (req.request_uri, fn, ctx, req, res)
|
||||
else
|
||||
respond_file (f, ctx, req, res)
|
||||
end
|
||||
else
|
||||
respond_access_denied (uri, ctx, req, res)
|
||||
end
|
||||
else
|
||||
respond_not_found (uri, ctx, req, res)
|
||||
end
|
||||
end
|
||||
|
||||
respond_index (a_uri: READABLE_STRING_8; dn: READABLE_STRING_8; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
uri, s: STRING_8
|
||||
d: DIRECTORY
|
||||
l_files: LIST [STRING_8]
|
||||
do
|
||||
create d.make_open_read (dn)
|
||||
if attached directory_index_file (d) as f then
|
||||
respond_file (f, ctx, req, res)
|
||||
else
|
||||
uri := a_uri
|
||||
if not uri.is_empty and then uri [uri.count] /= '/' then
|
||||
uri.append_character ('/')
|
||||
end
|
||||
s := "[
|
||||
<html>
|
||||
<head>
|
||||
<title>Index for folder: $URI</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Index for $URI</h1>
|
||||
<ul>
|
||||
]"
|
||||
s.replace_substring_all ("$URI", uri)
|
||||
|
||||
from
|
||||
l_files := d.linear_representation
|
||||
l_files.start
|
||||
until
|
||||
l_files.after
|
||||
loop
|
||||
s.append ("<li><a href=%"" + uri + l_files.item_for_iteration + "%">" + l_files.item_for_iteration + "</a></li>%N")
|
||||
l_files.forth
|
||||
end
|
||||
s.append ("[
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
]"
|
||||
)
|
||||
|
||||
create h.make
|
||||
h.put_content_type_text_html
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
h.put_content_length (s.count)
|
||||
res.write_header_text (h.string)
|
||||
if not req.request_method.same_string ({HTTP_REQUEST_METHODS}.method_head) then
|
||||
res.write_string (s)
|
||||
end
|
||||
res.flush
|
||||
end
|
||||
d.close
|
||||
end
|
||||
|
||||
respond_file (f: FILE; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
ext: READABLE_STRING_8
|
||||
ct: detachable READABLE_STRING_8
|
||||
fres: WSF_FILE_RESPONSE
|
||||
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))
|
||||
|
||||
res.put_response (fres)
|
||||
end
|
||||
|
||||
respond_not_found (uri: READABLE_STRING_8; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
s: STRING_8
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
create s.make_empty
|
||||
s.append ("Resource %"" + uri + "%" not found%N")
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.not_found)
|
||||
h.put_content_length (s.count)
|
||||
res.write_header_text (h.string)
|
||||
res.write_string (s)
|
||||
res.flush
|
||||
end
|
||||
|
||||
respond_access_denied (uri: READABLE_STRING_8; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
s: STRING_8
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_text_plain
|
||||
create s.make_empty
|
||||
s.append ("Resource %"" + uri + "%": Access denied%N")
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.forbidden)
|
||||
h.put_content_length (s.count)
|
||||
res.write_header_text (h.string)
|
||||
res.write_string (s)
|
||||
res.flush
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
directory_index_file (d: DIRECTORY): detachable FILE
|
||||
local
|
||||
f: detachable RAW_FILE
|
||||
fn: FILE_NAME
|
||||
do
|
||||
if attached directory_index as default_index then
|
||||
across
|
||||
default_index as c
|
||||
until
|
||||
Result /= Void
|
||||
loop
|
||||
if d.has_entry (c.item) then
|
||||
create fn.make_from_string (d.name)
|
||||
fn.set_file_name (c.item)
|
||||
if f = Void then
|
||||
create f.make (fn.string)
|
||||
else
|
||||
f.make (fn.string)
|
||||
end
|
||||
if f.exists and then f.is_readable then
|
||||
Result := f
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
resource_filename (uri: READABLE_STRING_8): READABLE_STRING_8
|
||||
do
|
||||
Result := real_filename (document_root + real_filename (uri))
|
||||
end
|
||||
|
||||
dirname (uri: READABLE_STRING_8): READABLE_STRING_8
|
||||
local
|
||||
p: INTEGER
|
||||
do
|
||||
p := uri.last_index_of ('/', uri.count)
|
||||
if p > 0 then
|
||||
Result := uri.substring (1, p - 1)
|
||||
else
|
||||
create {STRING_8} Result.make_empty
|
||||
end
|
||||
end
|
||||
|
||||
filename (uri: READABLE_STRING_8): READABLE_STRING_8
|
||||
local
|
||||
p: INTEGER
|
||||
do
|
||||
p := uri.last_index_of ('/', uri.count)
|
||||
if p > 0 then
|
||||
Result := uri.substring (p + 1, uri.count)
|
||||
else
|
||||
Result := uri.twin
|
||||
end
|
||||
end
|
||||
|
||||
extension (uri: READABLE_STRING_8): READABLE_STRING_8
|
||||
local
|
||||
p: INTEGER
|
||||
do
|
||||
p := uri.last_index_of ('.', uri.count)
|
||||
if p > 0 then
|
||||
Result := uri.substring (p + 1, uri.count)
|
||||
else
|
||||
create {STRING_8} Result.make_empty
|
||||
end
|
||||
end
|
||||
|
||||
real_filename (fn: STRING): STRING
|
||||
-- Real filename from url-path `fn'
|
||||
--| Find a better design for this piece of code
|
||||
--| Eventually in a spec/$ISE_PLATFORM/ specific cluster
|
||||
do
|
||||
if fn.is_empty then
|
||||
Result := fn
|
||||
else
|
||||
if {PLATFORM}.is_windows then
|
||||
create Result.make_from_string (fn)
|
||||
Result.replace_substring_all ("/", "\")
|
||||
if Result [Result.count] = '\' then
|
||||
Result.remove_tail (1)
|
||||
end
|
||||
else
|
||||
Result := fn
|
||||
if Result [Result.count] = '/' then
|
||||
Result.remove_tail (1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
node_exists (p: READABLE_STRING_8): BOOLEAN
|
||||
local
|
||||
f: RAW_FILE
|
||||
do
|
||||
create f.make (p)
|
||||
Result := f.exists
|
||||
end
|
||||
|
||||
extension_mime_mapping: HTTP_FILE_EXTENSION_MIME_MAPPING
|
||||
local
|
||||
f: RAW_FILE
|
||||
once
|
||||
create f.make ("mime.types")
|
||||
if f.exists and then f.is_readable then
|
||||
create Result.make_from_file (f.name)
|
||||
else
|
||||
create Result.make_default
|
||||
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
|
||||
85
library/server/wsf/router/request_handler.e
Normal file
85
library/server/wsf/router/request_handler.e
Normal file
@@ -0,0 +1,85 @@
|
||||
note
|
||||
description: "Summary description for {REQUEST_HANDLER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
REQUEST_HANDLER [C -> REQUEST_HANDLER_CONTEXT]
|
||||
|
||||
inherit
|
||||
ANY
|
||||
|
||||
ROUTED_SERVICE_HELPER
|
||||
export
|
||||
{NONE} all
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_valid_context (req: WSF_REQUEST): BOOLEAN
|
||||
-- Is `req' valid context for current handler?
|
||||
do
|
||||
Result := True
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
require
|
||||
is_valid_context: is_valid_context (req)
|
||||
deferred
|
||||
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 {REQUEST_ROUTER} -- Routes change
|
||||
|
||||
on_handler_mapped (a_resource: READABLE_STRING_8; a_rqst_methods: detachable ARRAY [READABLE_STRING_8])
|
||||
-- Callback called when a router map a route to Current handler
|
||||
do
|
||||
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
|
||||
170
library/server/wsf/router/request_handler_context.e
Normal file
170
library/server/wsf/router/request_handler_context.e
Normal file
@@ -0,0 +1,170 @@
|
||||
note
|
||||
description: "Summary description for {REQUEST_HANDLER_CONTEXT}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
REQUEST_HANDLER_CONTEXT
|
||||
|
||||
inherit
|
||||
ANY
|
||||
|
||||
REQUEST_FORMAT_UTILITY
|
||||
export
|
||||
{NONE} all
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
request: WSF_REQUEST
|
||||
-- Associated request
|
||||
|
||||
path: READABLE_STRING_8
|
||||
-- Associated path
|
||||
|
||||
feature {NONE} -- Constants
|
||||
|
||||
Format_constants: HTTP_FORMAT_CONSTANTS
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
feature -- Query
|
||||
|
||||
request_format (a_format_variable_name: detachable READABLE_STRING_8; content_type_supported: detachable ARRAY [READABLE_STRING_8]): detachable READABLE_STRING_8
|
||||
-- Format id for the request based on {HTTP_FORMAT_CONSTANTS}
|
||||
do
|
||||
if a_format_variable_name /= Void and then attached string_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 READABLE_STRING_8; content_type_supported: detachable ARRAY [READABLE_STRING_8]): 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_constants.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_MIME_TYPES}.text_json) then
|
||||
Result := {HTTP_FORMAT_CONSTANTS}.json_name
|
||||
elseif a_content_type.same_string ({HTTP_MIME_TYPES}.application_json) then
|
||||
Result := {HTTP_FORMAT_CONSTANTS}.json_name
|
||||
elseif a_content_type.same_string ({HTTP_MIME_TYPES}.text_xml) then
|
||||
Result := {HTTP_FORMAT_CONSTANTS}.xml_name
|
||||
elseif a_content_type.same_string ({HTTP_MIME_TYPES}.text_html) then
|
||||
Result := {HTTP_FORMAT_CONSTANTS}.html_name
|
||||
elseif a_content_type.same_string ({HTTP_MIME_TYPES}.text_plain) then
|
||||
Result := {HTTP_FORMAT_CONSTANTS}.text_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
request_content_type (content_type_supported: detachable ARRAY [READABLE_STRING_8]): detachable READABLE_STRING_8
|
||||
local
|
||||
s: detachable READABLE_STRING_8
|
||||
i,n: INTEGER
|
||||
l_accept_lst: detachable ARRAYED_LIST [READABLE_STRING_8]
|
||||
do
|
||||
l_accept_lst := accepted_content_types (request)
|
||||
if attached request.content_type as ct then
|
||||
if l_accept_lst /= Void then
|
||||
l_accept_lst.put_front (ct)
|
||||
else
|
||||
Result := ct
|
||||
end
|
||||
end
|
||||
if Result = Void and l_accept_lst /= Void then
|
||||
from
|
||||
l_accept_lst.start
|
||||
until
|
||||
l_accept_lst.after or Result /= Void
|
||||
loop
|
||||
s := l_accept_lst.item
|
||||
if content_type_supported /= Void then
|
||||
from
|
||||
i := content_type_supported.lower
|
||||
n := content_type_supported.upper
|
||||
until
|
||||
i > n or Result /= Void
|
||||
loop
|
||||
if content_type_supported[i].same_string (s) then
|
||||
Result := s
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
Result := s
|
||||
end
|
||||
l_accept_lst.forth
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Query
|
||||
|
||||
path_parameter (a_name: READABLE_STRING_8): detachable WSF_VALUE
|
||||
-- Parameter value for path variable `a_name'
|
||||
deferred
|
||||
end
|
||||
|
||||
query_parameter (a_name: READABLE_STRING_8): detachable WSF_VALUE
|
||||
-- Parameter value for query variable `a_name'
|
||||
--| i.e after the ? character
|
||||
deferred
|
||||
end
|
||||
|
||||
parameter (a_name: READABLE_STRING_8): detachable WSF_VALUE
|
||||
-- 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
|
||||
|
||||
feature -- String query
|
||||
|
||||
string_from (a_value: detachable WSF_VALUE): detachable READABLE_STRING_32
|
||||
do
|
||||
if attached {WSF_STRING} a_value as val then
|
||||
Result := val.string
|
||||
end
|
||||
end
|
||||
|
||||
string_path_parameter (a_name: READABLE_STRING_8): detachable READABLE_STRING_32
|
||||
do
|
||||
Result := string_from (path_parameter (a_name))
|
||||
end
|
||||
|
||||
string_query_parameter (a_name: READABLE_STRING_8): detachable READABLE_STRING_32
|
||||
do
|
||||
Result := string_from (query_parameter (a_name))
|
||||
end
|
||||
|
||||
string_parameter (a_name: READABLE_STRING_8): detachable READABLE_STRING_32
|
||||
do
|
||||
Result := string_from (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
|
||||
199
library/server/wsf/router/request_router.e
Normal file
199
library/server/wsf/router/request_router.e
Normal file
@@ -0,0 +1,199 @@
|
||||
note
|
||||
description: "Summary description for {REQUEST_ROUTER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
REQUEST_ROUTER [H -> REQUEST_HANDLER [C], C -> REQUEST_HANDLER_CONTEXT]
|
||||
|
||||
feature -- Mapping
|
||||
|
||||
map_default (h: like default_handler)
|
||||
-- Map default handler
|
||||
-- If no route/handler is found,
|
||||
-- then use `default_handler' as default if not Void
|
||||
do
|
||||
set_default_handler (h)
|
||||
end
|
||||
|
||||
map (a_resource: READABLE_STRING_8; h: H)
|
||||
-- Map handler `h' with `a_resource'
|
||||
do
|
||||
map_with_request_methods (a_resource, h, Void)
|
||||
end
|
||||
|
||||
map_routing (a_resource: READABLE_STRING_8; h: H)
|
||||
-- Map handler `h' with `a_resource'
|
||||
do
|
||||
map (a_resource, h)
|
||||
end
|
||||
|
||||
map_with_request_methods (a_resource: READABLE_STRING_8; h: H; rqst_methods: detachable ARRAY [READABLE_STRING_8])
|
||||
-- Map handler `h' with `a_resource' and `rqst_methods'
|
||||
deferred
|
||||
end
|
||||
|
||||
map_agent (a_resource: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: C; 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 ARRAY [READABLE_STRING_8])
|
||||
local
|
||||
rah: REQUEST_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
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Base url
|
||||
|
||||
base_url: detachable READABLE_STRING_8
|
||||
-- Common start of any route url
|
||||
|
||||
set_base_url (a_base_url: like base_url)
|
||||
-- Set `base_url' to `a_base_url'
|
||||
do
|
||||
base_url := a_base_url
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
dispatch (req: WSF_REQUEST; res: WSF_RESPONSE): 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: 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.
|
||||
local
|
||||
d: like handler
|
||||
ctx: detachable like default_handler_context
|
||||
do
|
||||
d := handler (req)
|
||||
if d /= Void then
|
||||
Result := d.handler
|
||||
ctx := d.context
|
||||
else
|
||||
Result := default_handler
|
||||
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: H; 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
|
||||
|
||||
source_uri (req: WSF_REQUEST): READABLE_STRING_32
|
||||
-- URI to use to find handler.
|
||||
do
|
||||
Result := req.path_info
|
||||
end
|
||||
|
||||
handler (req: WSF_REQUEST): detachable TUPLE [handler: H; context: like default_handler_context]
|
||||
-- Handler whose map matched with `req'
|
||||
require
|
||||
req_valid: source_uri (req) /= Void
|
||||
deferred
|
||||
ensure
|
||||
source_uri_unchanged: source_uri (req).same_string (old source_uri (req))
|
||||
end
|
||||
|
||||
is_matching_request_methods (a_request_method: READABLE_STRING_GENERAL; rqst_methods: like formatted_request_methods): BOOLEAN
|
||||
-- `a_request_method' is matching `rqst_methods' contents
|
||||
local
|
||||
i,n: INTEGER
|
||||
m: READABLE_STRING_GENERAL
|
||||
do
|
||||
if rqst_methods /= Void and then not rqst_methods.is_empty then
|
||||
m := a_request_method
|
||||
from
|
||||
i := rqst_methods.lower
|
||||
n := rqst_methods.upper
|
||||
until
|
||||
i > n or Result
|
||||
loop
|
||||
Result := m.same_string (rqst_methods[i])
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
|
||||
formatted_request_methods (rqst_methods: like formatted_request_methods): detachable ARRAY [READABLE_STRING_8]
|
||||
-- Formatted request methods values
|
||||
local
|
||||
i,l,u: INTEGER
|
||||
do
|
||||
if rqst_methods /= Void and then not rqst_methods.is_empty then
|
||||
l := rqst_methods.lower
|
||||
u := rqst_methods.upper
|
||||
create Result.make_filled (rqst_methods[l], l, u)
|
||||
from
|
||||
i := l + 1
|
||||
until
|
||||
i > u
|
||||
loop
|
||||
Result[i] := rqst_methods[i].as_string_8.as_upper
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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 H
|
||||
-- Default handler
|
||||
deferred
|
||||
end
|
||||
|
||||
default_handler_context (req: WSF_REQUEST): C
|
||||
-- Default handler context associated with `default_handler'
|
||||
require
|
||||
has_default_handler: default_handler /= Void
|
||||
deferred
|
||||
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
|
||||
73
library/server/wsf/router/request_routing_handler.e
Normal file
73
library/server/wsf/router/request_routing_handler.e
Normal file
@@ -0,0 +1,73 @@
|
||||
note
|
||||
description: "Summary description for {REQUEST_ROUTING_HANDLER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
REQUEST_ROUTING_HANDLER [H -> REQUEST_HANDLER [C],
|
||||
C -> REQUEST_HANDLER_CONTEXT]
|
||||
|
||||
inherit
|
||||
REQUEST_HANDLER [C]
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
local
|
||||
hdl: detachable H
|
||||
do
|
||||
hdl := router.dispatch_and_return_handler (req, res)
|
||||
end
|
||||
|
||||
feature {NONE} -- Routing
|
||||
|
||||
router: REQUEST_ROUTER [H, C]
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Mapping
|
||||
|
||||
map_default (h: detachable H)
|
||||
-- Map default handler
|
||||
-- If no route/handler is found,
|
||||
-- then use `default_handler' as default if not Void
|
||||
do
|
||||
router.map_default (h)
|
||||
end
|
||||
|
||||
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 ARRAY [READABLE_STRING_8])
|
||||
-- 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 ARRAY [READABLE_STRING_8])
|
||||
do
|
||||
router.map_agent_with_request_methods (a_resource, a_action, rqst_methods)
|
||||
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
|
||||
75
library/server/wsf/router/routed_service_i.e
Normal file
75
library/server/wsf/router/routed_service_i.e
Normal file
@@ -0,0 +1,75 @@
|
||||
note
|
||||
description: "Summary description for {ROUTED_SERVICE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
ROUTED_SERVICE_I [H -> REQUEST_HANDLER [C], C -> REQUEST_HANDLER_CONTEXT]
|
||||
|
||||
feature -- Setup
|
||||
|
||||
initialize_router
|
||||
-- Initialize `router'
|
||||
do
|
||||
create_router
|
||||
setup_router
|
||||
end
|
||||
|
||||
create_router
|
||||
-- Create `router'
|
||||
deferred
|
||||
ensure
|
||||
router_created: router /= Void
|
||||
end
|
||||
|
||||
setup_router
|
||||
-- Setup `router'
|
||||
require
|
||||
router_created: router /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
router: REQUEST_ROUTER [H, C]
|
||||
-- Request router
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
l_handled: BOOLEAN
|
||||
rescued: BOOLEAN
|
||||
do
|
||||
if not rescued then
|
||||
l_handled := router.dispatch (req, res)
|
||||
if not l_handled then
|
||||
execute_default (req, res)
|
||||
end
|
||||
else
|
||||
execute_rescue (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
deferred
|
||||
end
|
||||
|
||||
execute_rescue (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
if not res.header_committed then
|
||||
res.write_header ({HTTP_STATUS_CODE}.internal_server_error, Void)
|
||||
end
|
||||
res.flush
|
||||
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
|
||||
39
library/server/wsf/router/uri/default/request_uri_router.e
Normal file
39
library/server/wsf/router/uri/default/request_uri_router.e
Normal file
@@ -0,0 +1,39 @@
|
||||
note
|
||||
description: "Summary description for {DEFAULT_REQUEST_URI_ROUTER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
REQUEST_URI_ROUTER
|
||||
|
||||
inherit
|
||||
REQUEST_URI_ROUTER_I [REQUEST_HANDLER [REQUEST_URI_HANDLER_CONTEXT], REQUEST_URI_HANDLER_CONTEXT]
|
||||
redefine
|
||||
map_agent_with_request_methods
|
||||
end
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Mapping
|
||||
|
||||
map_agent_with_request_methods (a_id: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: REQUEST_URI_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE]];
|
||||
rqst_methods: detachable ARRAY [READABLE_STRING_8])
|
||||
local
|
||||
h: REQUEST_AGENT_HANDLER [REQUEST_URI_HANDLER_CONTEXT]
|
||||
do
|
||||
create h.make (a_action)
|
||||
map_with_request_methods (a_id, h, rqst_methods)
|
||||
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,33 @@
|
||||
note
|
||||
description: "Summary description for {DEFAULT_REQUEST_URI_ROUTING_HANDLER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
REQUEST_URI_ROUTING_HANDLER
|
||||
|
||||
inherit
|
||||
REQUEST_URI_ROUTING_HANDLER_I [REQUEST_HANDLER [REQUEST_URI_HANDLER_CONTEXT], REQUEST_URI_HANDLER_CONTEXT]
|
||||
redefine
|
||||
router
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Routing
|
||||
|
||||
router: REQUEST_URI_ROUTER
|
||||
|
||||
;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
|
||||
30
library/server/wsf/router/uri/default/uri_routed_service.e
Normal file
30
library/server/wsf/router/uri/default/uri_routed_service.e
Normal file
@@ -0,0 +1,30 @@
|
||||
note
|
||||
description: "Summary description for {DEFAULT_URI_ROUTED_SERVICE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
URI_ROUTED_SERVICE
|
||||
|
||||
inherit
|
||||
ROUTED_SERVICE_I [REQUEST_HANDLER [REQUEST_URI_HANDLER_CONTEXT], REQUEST_URI_HANDLER_CONTEXT]
|
||||
redefine
|
||||
router
|
||||
end
|
||||
|
||||
feature -- Router
|
||||
|
||||
router: REQUEST_URI_ROUTER
|
||||
|
||||
;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
|
||||
45
library/server/wsf/router/uri/request_uri_handler_context.e
Normal file
45
library/server/wsf/router/uri/request_uri_handler_context.e
Normal file
@@ -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: WSF_REQUEST; p: like path)
|
||||
do
|
||||
request := req
|
||||
path := p
|
||||
end
|
||||
|
||||
feature -- Query
|
||||
|
||||
path_parameter (a_name: READABLE_STRING_8): detachable WSF_VALUE
|
||||
do
|
||||
end
|
||||
|
||||
query_parameter (a_name: READABLE_STRING_8): detachable WSF_VALUE
|
||||
do
|
||||
Result := request.query_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
|
||||
202
library/server/wsf/router/uri/request_uri_router_i.e
Normal file
202
library/server/wsf/router/uri/request_uri_router_i.e
Normal file
@@ -0,0 +1,202 @@
|
||||
note
|
||||
description: "Summary description for {REQUEST_URI_ROUTER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
REQUEST_URI_ROUTER_I [H -> REQUEST_HANDLER [C], C -> REQUEST_URI_HANDLER_CONTEXT create make end]
|
||||
|
||||
inherit
|
||||
REQUEST_ROUTER [H, C]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (n: INTEGER)
|
||||
do
|
||||
create handlers.make (n)
|
||||
handlers.compare_objects
|
||||
end
|
||||
|
||||
feature -- Registration
|
||||
|
||||
map_with_request_methods (p: READABLE_STRING_8; h: H; rqst_methods: detachable ARRAY [READABLE_STRING_8])
|
||||
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, formatted_request_methods (rqst_methods)])
|
||||
h.on_handler_mapped (l_uri, rqst_methods)
|
||||
end
|
||||
|
||||
feature {NONE} -- Access: Implementation
|
||||
|
||||
handler (req: WSF_REQUEST): detachable TUPLE [handler: H; context: like default_handler_context]
|
||||
local
|
||||
h: detachable H
|
||||
ctx: detachable like default_handler_context
|
||||
do
|
||||
h := handler_by_path (source_uri (req), req.request_method)
|
||||
if h = Void then
|
||||
if attached smart_handler_by_path (source_uri (req), req.request_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
|
||||
Result := [h, ctx]
|
||||
else
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
smart_handler (req: WSF_REQUEST): detachable TUPLE [path: READABLE_STRING_8; handler: H]
|
||||
require
|
||||
req_valid: req /= Void and then source_uri (req) /= Void
|
||||
do
|
||||
Result := smart_handler_by_path (source_uri (req), req.request_method)
|
||||
ensure
|
||||
req_path_info_unchanged: source_uri (req).same_string (old source_uri (req))
|
||||
end
|
||||
|
||||
handler_by_path (a_path: READABLE_STRING_GENERAL; rqst_method: READABLE_STRING_GENERAL): 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_GENERAL): 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 ARRAY [READABLE_STRING_8]]]
|
||||
-- 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 ARRAY [READABLE_STRING_8]]]
|
||||
-- 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
|
||||
|
||||
feature {NONE} -- Default: implementation
|
||||
|
||||
default_handler: detachable H
|
||||
|
||||
set_default_handler (h: like default_handler)
|
||||
do
|
||||
default_handler := h
|
||||
end
|
||||
|
||||
default_handler_context (req: WSF_REQUEST): C
|
||||
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: "[
|
||||
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,38 @@
|
||||
note
|
||||
description: "Summary description for {REQUEST_ROUTING_HANDLER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
REQUEST_URI_ROUTING_HANDLER_I [H -> REQUEST_HANDLER [C],
|
||||
C -> REQUEST_URI_HANDLER_CONTEXT create make end]
|
||||
|
||||
inherit
|
||||
REQUEST_ROUTING_HANDLER [H, C]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (n: INTEGER)
|
||||
do
|
||||
create router.make (n)
|
||||
end
|
||||
|
||||
feature {NONE} -- Routing
|
||||
|
||||
router: REQUEST_URI_ROUTER_I [H, C]
|
||||
|
||||
;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,40 @@
|
||||
note
|
||||
description: "Summary description for {DEFAULT_REQUEST_URI_TEMPLATE_ROUTER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
REQUEST_URI_TEMPLATE_ROUTER
|
||||
|
||||
inherit
|
||||
REQUEST_URI_TEMPLATE_ROUTER_I [REQUEST_HANDLER [REQUEST_URI_TEMPLATE_HANDLER_CONTEXT], REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
|
||||
redefine
|
||||
map_agent_with_request_methods
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Mapping
|
||||
|
||||
map_agent_with_request_methods (a_id: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: REQUEST_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE]];
|
||||
rqst_methods: detachable ARRAY [READABLE_STRING_8])
|
||||
local
|
||||
h: REQUEST_AGENT_HANDLER [REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
|
||||
do
|
||||
create h.make (a_action)
|
||||
map_with_request_methods (a_id, h, rqst_methods)
|
||||
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,33 @@
|
||||
note
|
||||
description: "Summary description for {DEFAULT_REQUEST_URI_TEMPLATE_ROUTING_HANDLER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
REQUEST_URI_TEMPLATE_ROUTING_HANDLER
|
||||
|
||||
inherit
|
||||
REQUEST_URI_TEMPLATE_ROUTING_HANDLER_I [REQUEST_HANDLER [REQUEST_URI_TEMPLATE_HANDLER_CONTEXT], REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
|
||||
redefine
|
||||
router
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Routing
|
||||
|
||||
router: REQUEST_URI_TEMPLATE_ROUTER
|
||||
|
||||
;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,30 @@
|
||||
note
|
||||
description: "Summary description for {DEFAULT_URI_TEMPLATE_ROUTED_SERVICE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
URI_TEMPLATE_ROUTED_SERVICE
|
||||
|
||||
inherit
|
||||
ROUTED_SERVICE_I [REQUEST_HANDLER [REQUEST_URI_TEMPLATE_HANDLER_CONTEXT], REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
|
||||
redefine
|
||||
router
|
||||
end
|
||||
|
||||
feature -- Router
|
||||
|
||||
router: REQUEST_URI_TEMPLATE_ROUTER
|
||||
|
||||
;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,60 @@
|
||||
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: WSF_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: READABLE_STRING_8): detachable WSF_VALUE
|
||||
do
|
||||
if attached uri_template_match.url_decoded_path_variable (a_name) as s then
|
||||
create {WSF_STRING} Result.make (a_name, s)
|
||||
end
|
||||
end
|
||||
|
||||
query_parameter (a_name: READABLE_STRING_8): detachable WSF_VALUE
|
||||
do
|
||||
if attached uri_template_match.url_decoded_query_variable (a_name) as s then
|
||||
create {WSF_STRING} Result.make (a_name, s)
|
||||
else
|
||||
Result := request.query_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,169 @@
|
||||
note
|
||||
description: "Summary description for {REQUEST_URI_TEMPLATE_ROUTER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
REQUEST_URI_TEMPLATE_ROUTER_I [H -> REQUEST_HANDLER [C], C -> REQUEST_URI_TEMPLATE_HANDLER_CONTEXT create make end]
|
||||
|
||||
inherit
|
||||
REQUEST_ROUTER [H, C]
|
||||
|
||||
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: 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 ARRAY [READABLE_STRING_8])
|
||||
local
|
||||
l_tpl: like {URI_TEMPLATE}.template
|
||||
l_uri: URI_TEMPLATE
|
||||
do
|
||||
l_uri := uri
|
||||
l_tpl := l_uri.template
|
||||
if attached base_url as l_base_url then
|
||||
l_uri := l_uri.duplicate
|
||||
l_uri.set_template (l_base_url + l_tpl)
|
||||
l_tpl := l_uri.template
|
||||
end
|
||||
|
||||
handlers.force ([h, l_tpl, formatted_request_methods (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 ARRAY [READABLE_STRING_8])
|
||||
do
|
||||
map_with_uri_template_and_request_methods (create {URI_TEMPLATE}.make (tpl), h, rqst_methods)
|
||||
end
|
||||
|
||||
feature {NONE} -- Access: Implementation
|
||||
|
||||
handler (req: WSF_REQUEST): detachable TUPLE [handler: attached like default_handler; context: like default_handler_context]
|
||||
local
|
||||
l_handlers: like handlers
|
||||
t: READABLE_STRING_8
|
||||
p: READABLE_STRING_8
|
||||
l_req_method: READABLE_STRING_GENERAL
|
||||
l_res: URI_TEMPLATE_MATCH_RESULT
|
||||
do
|
||||
p := source_uri (req)
|
||||
from
|
||||
l_req_method := req.request_method
|
||||
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 {REQUEST_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")
|
||||
|
||||
Result := [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
|
||||
Result := [l_info.handler, handler_context (p, req, tpl, res)]
|
||||
end
|
||||
end
|
||||
end
|
||||
l_handlers.forth
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Context factory
|
||||
|
||||
handler_context (p: detachable STRING; 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 ARRAY [READABLE_STRING_8]]]
|
||||
-- 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 ARRAY [READABLE_STRING_8]]]
|
||||
-- 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
|
||||
|
||||
feature {NONE} -- Default: implementation
|
||||
|
||||
default_handler: detachable H
|
||||
|
||||
set_default_handler (h: like default_handler)
|
||||
do
|
||||
default_handler := h
|
||||
end
|
||||
|
||||
default_handler_context (req: WSF_REQUEST): C
|
||||
do
|
||||
Result := handler_context (Void, req, create {URI_TEMPLATE}.make ("/"), create {URI_TEMPLATE_MATCH_RESULT}.make_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,38 @@
|
||||
note
|
||||
description: "Summary description for {REQUEST_ROUTING_HANDLER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
REQUEST_URI_TEMPLATE_ROUTING_HANDLER_I [H -> REQUEST_HANDLER [C],
|
||||
C -> REQUEST_URI_TEMPLATE_HANDLER_CONTEXT create make end]
|
||||
|
||||
inherit
|
||||
REQUEST_ROUTING_HANDLER [H, C]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (n: INTEGER)
|
||||
do
|
||||
create router.make (n)
|
||||
end
|
||||
|
||||
feature {NONE} -- Routing
|
||||
|
||||
router: REQUEST_URI_TEMPLATE_ROUTER_I [H, C]
|
||||
|
||||
;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
|
||||
@@ -10,11 +10,14 @@
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
</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"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<library name="error" location="..\..\error\error-safe.ecf"/>
|
||||
<library name="http" location="..\..\protocol\http\http-safe.ecf"/>
|
||||
<library name="uri_template" location="..\..\protocol\uri_template\uri_template-safe.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||
<cluster name="src" location=".\src" recursive="true"/>
|
||||
<cluster name="router" location=".\router" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -10,11 +10,14 @@
|
||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
|
||||
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
|
||||
<library name="ewsgi" location="..\ewsgi\ewsgi.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<library name="error" location="..\..\error\error.ecf"/>
|
||||
<library name="http" location="..\..\protocol\http\http.ecf"/>
|
||||
<library name="uri_template" location="..\..\protocol\uri_template\uri_template.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||
<cluster name="src" location=".\src" recursive="true"/>
|
||||
<cluster name="router" location=".\router" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
Reference in New Issue
Block a user