Merge remote-tracking branch 'jocelynEWF/master'

This commit is contained in:
jvelilla
2011-09-14 07:58:40 -03:00
74 changed files with 3657 additions and 267 deletions

View File

@@ -1,5 +1,15 @@
History for Eiffel-Web-Framework
[2011-09-13] Jocelyn
* library "router": now using a generic design to allow customization of
request handler context class.
* library "server/request/rest": first attempt to provide a library to
help building RESTful application (the interfaces are likely to change
soon) EXPERIMENTAL
[2011-09-09] Jocelyn
* library "uri-template": better support for {/vars} and {?vars}
[2011-09-07] Jocelyn
* library "router": now routing depends on uri (or uri template) and request methods
* Nino connector: Fixed issue where HTTP_ prefix were missing for header meta variable.

View File

@@ -10,7 +10,7 @@ class
inherit
ANY
ROUTED_APPLICATION
DEFAULT_URI_TEMPLATE_ROUTED_APPLICATION
ROUTED_APPLICATION_HELPER
@@ -30,29 +30,33 @@ feature {NONE} -- Initialization
create_router
do
debug
create {REQUEST_URI_ROUTER} router.make (5)
create {REQUEST_URI_TEMPLATE_ROUTER} router.make (5)
end
-- create {REQUEST_URI_ROUTER} router.make (5)
create {REQUEST_URI_TEMPLATE_ROUTER} router.make (5)
create router.make (5)
end
setup_router
local
ra: REQUEST_AGENT_HANDLER
ra: REQUEST_AGENT_HANDLER [REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
rag: DEFAULT_REQUEST_URI_TEMPLATE_ROUTING_HANDLER
do
router.map_agent ("/home", agent execute_home)
create rag.make (3)
create ra.make (agent handle_hello)
router.map ("/hello/{name}.{format}", ra)
router.map ("/hello.{format}/{name}", ra)
router.map ("/hello/{name}", ra)
rag.map ("/hello/{name}.{format}", ra)
rag.map ("/hello.{format}/{name}", ra)
rag.map ("/hello/{name}", ra)
-- router.map ("/hello/{name}.{format}", ra)
-- router.map ("/hello.{format}/{name}", ra)
-- router.map ("/hello/{name}", ra)
create ra.make (agent handle_anonymous_hello)
router.map ("/hello", ra)
router.map ("/hello.{format}", ra)
rag.map ("/hello", ra)
rag.map ("/hello.{format}", ra)
-- router.map ("/hello", ra)
-- router.map ("/hello.{format}", ra)
router.map ("/hello", rag)
router.map_agent_with_request_methods ("/method/any", agent handle_method_any, Void)
router.map_agent_with_request_methods ("/method/guess", agent handle_method_get_or_post, <<"GET", "POST">>)
@@ -108,7 +112,7 @@ feature -- Execution
res.flush
end
execute_home (ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
execute_home (ctx: REQUEST_URI_TEMPLATE_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
res.write_header (200, <<["Content-Type", "text/html"]>>)
res.write_string ("<html><body>Hello World ?!%N")
@@ -141,7 +145,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}"
@@ -172,33 +176,33 @@ feature -- Execution
end
end
handle_hello (ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
handle_hello (ctx: REQUEST_URI_TEMPLATE_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
execute_hello (req, res, Void, ctx)
end
handle_anonymous_hello (ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
handle_anonymous_hello (ctx: REQUEST_URI_TEMPLATE_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
execute_hello (req, res, ctx.parameter ("name"), ctx)
end
handle_method_any (ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
handle_method_any (ctx: REQUEST_URI_TEMPLATE_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
execute_hello (req, res, req.request_method, ctx)
end
handle_method_get (ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
handle_method_get (ctx: REQUEST_URI_TEMPLATE_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
execute_hello (req, res, "GET", ctx)
end
handle_method_post (ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
handle_method_post (ctx: REQUEST_URI_TEMPLATE_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
execute_hello (req, res, "POST", ctx)
end
handle_method_get_or_post (ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
handle_method_get_or_post (ctx: REQUEST_URI_TEMPLATE_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
execute_hello (req, res, "GET or POST", ctx)
end

View File

@@ -38,11 +38,11 @@ feature -- Name
feature -- Query
format_id (a_id: STRING): INTEGER
format_id (a_id: READABLE_STRING_GENERAL): INTEGER
local
s: STRING
do
s := a_id.as_lower
s := a_id.as_string_8.as_lower
if s.same_string (json_name) then
Result := json
elseif s.same_string (xml_name) then
@@ -58,7 +58,7 @@ feature -- Query
end
end
format_name (a_id: INTEGER): STRING
format_name (a_id: INTEGER): READABLE_STRING_8
do
inspect a_id
when json then Result := json_name
@@ -74,7 +74,7 @@ feature -- Query
end
note
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -159,10 +159,10 @@ feature -- Match
b: BOOLEAN
tpl: like template
l_offset: INTEGER
p,q: INTEGER
p,q,nb: INTEGER
exp: URI_TEMPLATE_EXPRESSION
vn, s,t: STRING
vv: STRING
vv, path_vv: STRING
l_vars, l_path_vars, l_query_vars: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_GENERAL]
l_uri_count: INTEGER
tpl_count: INTEGER
@@ -196,7 +196,10 @@ feature -- Match
if p = q then
--| There should be at least one literal between two expression
--| {var}{foobar} is ambigous for matching ...
b := False
--| unless with {/var} or {?var} ... since we can search for '/' or '?'
exp.analyze
b := exp.operator = '/' or exp.operator = '?'
p := exp.end_position
elseif q > p then
if p = 0 then
p := 1
@@ -227,15 +230,42 @@ feature -- Match
else
if not l_expressions.after then
exp := l_expressions.item --| We change `exp' here
l_next_literal_separator := tpl.substring (p, exp.position -1)
exp.analyze
if exp.operator = '/' or exp.operator = '?' then
l_next_literal_separator := Void
else
l_next_literal_separator := tpl.substring (p, exp.position -1)
end
elseif p < tpl_count then
l_next_literal_separator := tpl.substring (p, tpl_count)
else
l_next_literal_separator := Void
end
vv := next_path_variable_value (a_uri, q + l_offset, l_next_literal_separator)
l_vars.force (vv, vn)
l_offset := l_offset + vv.count - (vn.count + 2)
if vn[1] = '/' then
vn := vn.substring (2, vn.count)
from
create path_vv.make_empty
vv := "/"
nb := 0
until
vv.is_empty or q + l_offset > a_uri.count
loop
vv := next_path_variable_value (a_uri, q + l_offset + 1, l_next_literal_separator)
l_offset := l_offset + vv.count + 1
nb := nb + 1
if not vv.is_empty then
path_vv.extend ('/')
path_vv.append (vv)
l_vars.force (vv, vn + "[" + nb.out + "]")
end
end
l_vars.force (path_vv, vn)
l_offset := l_offset - (1 + vn.count + 2)
else
vv := next_path_variable_value (a_uri, q + l_offset, l_next_literal_separator)
l_vars.force (vv, vn)
l_offset := l_offset + vv.count - (vn.count + 2)
end
end
else
b := exp.is_query --| query are optional

View File

@@ -23,7 +23,7 @@ feature -- Test routines
uri_template_parse ("weather/{state}/{city}?forecast={day}", <<"state", "city">>, <<"day">>)
uri_template_parse ("/hello/{name}.{format}", <<"name", "format">>, <<>>)
uri_template_parse ("/hello.{format}/{name}", <<"format", "name">>, <<>>)
uri_template_parse ("/hello/Joce.{format}/foo{?foobar};crazy=IDEA", <<"name">>, <<"foobar">>)
-- uri_template_parse ("/hello/{name}.{format}/foo{?foobar};crazy=IDEA", <<"name", "format">>, <<"foobar">>)
end
test_uri_template_matcher
@@ -32,6 +32,13 @@ feature -- Test routines
local
tpl: URI_TEMPLATE
do
create tpl.make ("/hello.{format}{/vars}")
uri_template_match (tpl, "/hello.json/foo/bar", <<["format", "json"], ["vars", "/foo/bar"], ["vars[1]", "foo"], ["vars[2]", "bar"]>>, <<>>)
create tpl.make ("/hello.{format}{?op}")
uri_template_match (tpl, "/hello.json?op=foobar", <<["format", "json"]>>, << ["op", "foobar"]>>)
create tpl.make ("{version}/{id}")
uri_template_match (tpl, "v2/123", <<["version", "v2"], ["id" , "123"]>>, <<>>)
@@ -73,10 +80,14 @@ feature -- Test routines
uri_template_match (tpl, "/hello/Joce.xml/foo", <<["name", "Joce"], ["format", "xml"]>>, <<>>)
uri_template_mismatch (tpl, "/hello/Joce.xml/fooBAR")
create tpl.make ("/hello/{name}.{format}/foo{?foo};crazy={idea}")
-- uri_template_match (tpl, "/hello/Joce.xml/foo", <<["name", "Joce"], ["format", "xml"]>>, <<>>)
uri_template_match (tpl, "/hello/Joce.xml/foo?foo=FOO", <<["name", "Joce"], ["format", "xml"]>>, <<["foo", "FOO"]>>)
uri_template_match (tpl, "/hello/Joce.xml/foo;crazy=IDEA", <<["name", "Joce"], ["format", "xml"]>>, <<["idea", "IDEA"], ["crazy", "IDEA"]>>)
create tpl.make ("/hello{/vars}")
uri_template_match (tpl, "/hello/foo/bar", <<["vars", "/foo/bar"], ["vars[1]", "foo"], ["vars[2]", "bar"]>>, <<>>)
-- create tpl.make ("/hello/{name}.{format}/foo{?foo};crazy={idea}")
---- uri_template_match (tpl, "/hello/Joce.xml/foo", <<["name", "Joce"], ["format", "xml"]>>, <<>>)
-- uri_template_match (tpl, "/hello/Joce.xml/foo?foo=FOO", <<["name", "Joce"], ["format", "xml"]>>, <<["foo", "FOO"]>>)
-- uri_template_match (tpl, "/hello/Joce.xml/foo;crazy=IDEA", <<["name", "Joce"], ["format", "xml"]>>, <<["idea", "IDEA"], ["crazy", "IDEA"]>>)
end

View File

@@ -15,14 +15,20 @@ feature {NONE} -- Initialization
local
app: NINO_APPLICATION
do
port_number := 8123
print ("Example: start a Nino web server on port " + port_number.out + ", %Nand reply Hello World for any request such as http://localhost:" + port_number.out + "/%N")
create app.make_custom (agent execute, "")
port_number := 80
base_url := ""
debug ("nino")
print ("Example: start a Nino web server on port " + port_number.out +
", %Nand reply Hello World for any request such as http://localhost:" + port_number.out + "/" + base_url + "%N")
end
create app.make_custom (agent execute, base_url)
app.listen (port_number)
end
port_number: INTEGER
base_url: STRING
invariant
port_number_valid: port_number > 0
note

View File

@@ -0,0 +1 @@
reference:forum2

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="rest" uuid="97FA9457-7820-4A21-8A13-C8D0E9CCEC62" library_target="rest">
<target name="rest">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi-safe.ecf"/>
<library name="http" location="..\..\..\protocol\http\http-safe.ecf"/>
<library name="router" location="..\..\request\router\router-safe.ecf" readonly="false"/>
<library name="uri_template" location="..\..\..\protocol\uri_template\uri_template-safe.ecf"/>
<cluster name="contrib" location=".\src\contrib\" recursive="true">
<file_rule>
<exclude>/html$</exclude>
</file_rule>
<cluster name="html" location="$|doc\html\" recursive="true" hidden="true"/>
</cluster>
<cluster name="src" location=".\src\" recursive="true">
<file_rule>
<exclude>/contrib$</exclude>
</file_rule>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="rest" uuid="97FA9457-7820-4A21-8A13-C8D0E9CCEC62" library_target="rest">
<target name="rest">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi.ecf"/>
<library name="http" location="..\..\..\protocol\http\http.ecf"/>
<library name="router" location="..\..\request\router\router.ecf" readonly="false"/>
<library name="uri_template" location="..\..\..\protocol\uri_template\uri_template.ecf"/>
<cluster name="contrib" location=".\src\contrib\" recursive="true">
<file_rule>
<exclude>/html$</exclude>
</file_rule>
<cluster name="html" location="$|doc\html\" recursive="true" hidden="true"/>
</cluster>
<cluster name="src" location=".\src\" recursive="true">
<file_rule>
<exclude>/contrib$</exclude>
</file_rule>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,31 @@
note
description: "Summary description for {REST_APPLICATION}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
REST_APPLICATION [H -> REST_REQUEST_HANDLER [C], C -> REST_REQUEST_HANDLER_CONTEXT]
inherit
ROUTED_APPLICATION [H, C]
redefine
router
end
feature -- Setup
router: REST_REQUEST_ROUTER [H, C]
-- Request router
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,78 @@
note
description: "Summary description for {REST_REQUEST_HANDLER_CONTEXT}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
REST_REQUEST_HANDLER_CONTEXT
inherit
REQUEST_HANDLER_CONTEXT
feature -- Status report
script_absolute_url (a_path: STRING): STRING
-- Absolute Url for the script if any, extended by `a_path'
do
Result := request.absolute_script_url (a_path)
end
script_url (a_path: STRING): STRING
-- Url relative to script name if any, extended by `a_path'
require
a_path_attached: a_path /= Void
do
Result := request.script_url (a_path)
end
url (args: detachable STRING; abs: BOOLEAN): STRING
-- Associated url based on `path' and `args'
-- if `abs' then return absolute url
local
s,t: detachable STRING
do
s := args
if s /= Void and then s.count > 0 then
create t.make_from_string (path)
if s[1] /= '/' then
t.append_character ('/')
t.append (s)
else
t.append (s)
end
s := t
else
s := path
end
if abs then
Result := script_absolute_url (s)
else
Result := script_url (s)
end
ensure
result_attached: Result /= Void
end
authenticated: BOOLEAN
do
if request.http_authorization /= Void then
Result := True
end
end
authenticated_identifier: detachable READABLE_STRING_32
do
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,28 @@
note
description: "Summary description for {REQUEST_URI_HANDLER_CONTEXT}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REST_REQUEST_URI_HANDLER_CONTEXT
inherit
REQUEST_URI_HANDLER_CONTEXT
REST_REQUEST_HANDLER_CONTEXT
create
make
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

View File

@@ -0,0 +1,28 @@
note
description: "Summary description for {REQUEST_URI_TEMPLATE_HANDLER_CONTEXT}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT
inherit
REQUEST_URI_TEMPLATE_HANDLER_CONTEXT
REST_REQUEST_HANDLER_CONTEXT
create
make
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

View File

@@ -0,0 +1,184 @@
note
description: "Summary description for {HTML_PAGE}."
date: "$Date$"
revision: "$Revision$"
class
HTML_PAGE
inherit
HTML_UTILITIES
create
make
feature {NONE} -- Initialization
make (a_title: like title)
do
create {ARRAYED_LIST [like html_attributes.item]} html_attributes.make (0)
create head.make (a_title)
create {ARRAYED_LIST [like body_attributes.item]} body_attributes.make (0)
create body.make_empty
initialize
end
initialize
-- Initialize
do
create headers.make
end
feature -- Access
headers: EWF_HEADER
feature -- Recycle
recycle
do
html_attributes.wipe_out
headers.recycle
head.recycle
body_attributes.wipe_out
body.wipe_out
internal_header_string := Void
internal_message_string := Void
end
feature -- Access
html_attributes: LIST [TUPLE [name: STRING; value: STRING]]
head: HTML_PAGE_HEAD
body_attributes: LIST [TUPLE [name: STRING; value: STRING]]
body: STRING
feature -- Query
title: detachable STRING
do
Result := head.title
end
add_html_ttribute (a: like html_attributes.item)
do
html_attributes.force (a)
end
add_body_attribute (a: like body_attributes.item)
do
body_attributes.force (a)
end
set_body (s: like body)
-- Set `body' to `s'
do
body := s
end
feature -- Element change
set_title (t: like title)
do
head.title := t
end
feature -- Output
compute
-- Compute the string output
local
s, t: STRING
do
--| HTML beginning
create s.make (128)
s.append_string ("<html")
s.append_string (attributes_to_string (html_attributes))
s.append_string (">%N")
--| Head
head.compute --| Be sure to have a fresh string
t := head.string
if not t.is_empty then
s.append_string (t)
s.append_character ('%N')
end
--| Body
s.append_string ("<body")
s.append_string (attributes_to_string (body_attributes))
s.append_string (">%N")
s.append_string (body)
s.append_string ("</body>")
--| End
s.append_string ("</html>")
--| Http headers
headers.put_content_length (s.count)
internal_header_string := headers.string
internal_message_string := s
end
header_string: STRING
local
o: like internal_header_string
do
o := internal_header_string
if o = Void then
compute
o := internal_header_string
if o = Void then
check output_computed: False end
create o.make_empty
end
end
Result := o
end
message_string: STRING
local
o: like internal_message_string
do
o := internal_message_string
if o = Void then
compute
o := internal_message_string
if o = Void then
check output_computed: False end
create o.make_empty
end
end
Result := o
end
send (buf: WGI_RESPONSE_BUFFER)
do
buf.set_status_code (200)
buf.write_headers_string (header_string)
buf.write_string (message_string)
end
feature {NONE} -- Implementation: output
internal_header_string: detachable like header_string
internal_message_string: detachable like message_string
invariant
header_attached: headers /= Void
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,111 @@
note
description: "Summary description for {HTML_PAGE_HEAD}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
HTML_PAGE_HEAD
inherit
HTML_UTILITIES
create
make
feature {NONE} -- Initialization
make (a_title: like title)
do
initialize
title := a_title
end
initialize
do
create text.make_empty
create {ARRAYED_LIST [like attributes.item]} attributes.make (0)
end
feature -- Recycle
recycle
do
attributes.wipe_out
title := Void
text.wipe_out
internal_string := Void
end
feature -- Access
attributes: LIST [TUPLE [name: STRING; value: STRING]]
title: detachable STRING assign set_title
text: STRING
feature -- Element change
set_title (t: like title)
do
title := t
end
feature -- Output
compute
-- Compute the string output
local
s: STRING
do
create s.make_empty
if attached title as t then
s.append_string ("<title>" + t + "</title>%N")
end
if text.count > 0 then
s.append_string (text)
s.append_character ('%N')
end
if s.count > 0 then
internal_string := "<head" + attributes_to_string (attributes) + ">%N" + s + "</head>"
else
internal_string := s
end
end
string: STRING
--
local
o: like internal_string
do
o := internal_string
if o = Void then
compute
o := internal_string
if o = Void then
check output_computed: False end
create o.make_empty
end
end
Result := o
end
feature {NONE} -- Implementation: output
internal_string: detachable like string
invariant
text_attached: text /= Void
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,90 @@
note
description: "Summary description for {HTML_UTILITIES}."
date: "$Date$"
revision: "$Revision$"
class
HTML_UTILITIES
feature -- Encoding
html_special_chars_encoded_string (s: STRING): STRING
local
c: CHARACTER
i,n: INTEGER
do
from
i := 1
n := s.count
create Result.make (n + 10)
until
i > n
loop
c := s[i]
inspect c
when '&' then
Result.append_string ("&amp;")
when '%"' then
Result.append_string ("&quot;")
when '%'' then
Result.append_string ("&#039;")
when '<' then
Result.append_string ("&lt;")
when '>' then
Result.append_string ("&gt;")
else
Result.extend (c)
end
i := i + 1
end
end
feature -- Helpers
strings_to_string (lst: LIST [STRING]; sep: STRING): STRING
do
create Result.make_empty
if lst.count > 0 then
from
lst.start
until
lst.after
loop
Result.append_string (lst.item)
Result.append_string (sep)
lst.forth
end
end
end
attributes_to_string (atts: LIST [TUPLE [name: STRING; value: STRING]]): STRING
do
create Result.make_empty
if atts.count > 0 then
from
atts.start
until
atts.after
loop
Result.append_character (' ')
Result.append_string (atts.item.name)
Result.append_character ('=')
Result.append_character ('%"')
Result.append_string (html_special_chars_encoded_string (atts.item.value))
Result.append_character ('%"')
atts.forth
end
end
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,317 @@
note
description: "Summary description for {REST_API_DOCUMENTATION}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REST_API_DOCUMENTATION [C -> REST_REQUEST_HANDLER_CONTEXT]
inherit
REST_REQUEST_HANDLER [C]
create
make
feature {NONE} -- Initialization
make (a_router: like router; a_base_doc_url: like base_doc_url)
do
router := a_router
base_doc_url := a_base_doc_url
description := "Technical documention for the API"
initialize
end
feature {NONE} -- Access: Implementation
router: REST_REQUEST_ROUTER [REST_REQUEST_HANDLER [C], C]
base_doc_url: READABLE_STRING_8
feature -- Access
authentication_required: BOOLEAN = False
feature -- Execution
execute_application (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
local
rep: like new_html_page
s: STRING
rq: detachable REST_REQUEST_HANDLER [C]
rq_resource: detachable READABLE_STRING_GENERAL
-- l_dft_format_name: detachable STRING
hdl_cursor: like router.new_cursor
do
rep := new_html_page
rep.headers.put_content_type_text_html
create s.make_empty
if attached ctx.path_parameter ("resource") as l_resource then
from
hdl_cursor := router.new_cursor
until
hdl_cursor.after or rq /= Void
loop
if hdl_cursor.item.resource.same_string_general (l_resource) then
rq := hdl_cursor.item.handler
rq_resource := l_resource
end
hdl_cursor.forth
end
end
-- if a_args /= Void and then not a_args.is_empty then
-- rq := router.handler_by_path (a_args)
-- if rq = Void then
-- rq := handler_manager.smart_handler_by_path (a_args)
---- if attached {REST_REQUEST_GROUP_HANDLER} rq as grp then
---- rq := grp.handlers.handler_by_path (a_args)
---- end
-- end
-- if
-- rq /= Void and then
-- attached rq.path_information (a_args) as l_info
-- then
-- l_dft_format_name := l_info.format
-- end
-- end
if rq /= Void and then rq_resource /= Void then
rep.set_big_title ("API: Technical documentation for ["+ rq_resource.as_string_8 +"]")
s.append_string ("<div class=%"api%">")
s.append_string ("<h2 class=%"api-name%" >")
s.append_string ("<a href=%"" + base_doc_url + "%">.. Show all features ..</a>")
s.append_string ("</h2></div>%N")
process_request_handler_doc (rq, rq_resource.as_string_8, s, ctx, req, res, Void)
else
rep.set_big_title ("API: Technical documentation")
from
hdl_cursor := router.new_cursor
until
hdl_cursor.after
loop
if attached hdl_cursor.item as l_item then
rq := l_item.handler
rep.add_shortcut (l_item.resource)
s.append ("<a name=%"" + rep.last_added_shortcut + "%"/>")
process_request_handler_doc (rq, l_item.resource, s, ctx, req, res, Void)
hdl_cursor.forth
end
end
end
rep.set_body (s)
rep.send (res)
rep.recycle
end
process_request_handler_doc (rq: REST_REQUEST_HANDLER [C]; a_resource: STRING; buf: STRING; ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER; a_dft_format: detachable STRING)
local
l_dft_format_name: detachable STRING
s: STRING
l_uri_tpl: URI_TEMPLATE
do
s := buf
if a_dft_format /= Void then
if rq.supported_format_names.has (a_dft_format) then
l_dft_format_name := a_dft_format
end
end
s.append_string ("<div class=%"api%">")
s.append_string ("<h2 class=%"api-name%" ><a href=%""+ url (req, base_doc_url, a_resource, False) +"%">"+ a_resource +"</a></h2>")
s.append_string ("<div class=%"inner%">")
-- if rq.hidden (req) then
-- s.append_string ("<div class=%"api-description%">This feature is hidden</div>%N")
-- else
if attached rq.description as desc then
s.append_string ("<div class=%"api-description%">" + desc + "</div>")
end
-- if attached {REST_REQUEST_GROUP_HANDLER} rq as grp then
-- s.append_string ("<div class=%"api-format%">Handler: <strong>")
-- if attached grp.handlers.new_cursor as l_handlers_cursor then
-- from
-- until
-- l_handlers_cursor.after
-- loop
-- s.append_string (" ")
-- s.append_string ("<a class=%"api-handler%" href=%"")
-- s.append_string (url (ctx, l_handlers_cursor.item.path, False))
-- s.append_string ("%">"+ l_handlers_cursor.item.path +"</a>")
-- l_handlers_cursor.forth
-- end
-- end
-- s.append_string ("</strong></div>")
-- end
if attached rq.supported_format_names as l_formats and then not l_formats.is_empty then
s.append_string ("<div class=%"api-format%">Supported formats: <strong>")
if attached l_formats.new_cursor as l_formats_cursor then
from
until
l_formats_cursor.after
loop
s.append_string (" ")
s.append_string ("<a class=%"api-name api-format")
if l_formats_cursor.item ~ l_dft_format_name then
s.append_string (" selected")
end
s.append_string ("%" href=%"" + url (req, base_doc_url, a_resource, False) + "." + l_formats_cursor.item + "%">"+ l_formats_cursor.item +"</a>")
l_formats_cursor.forth
end
end
s.append_string ("</strong></div>")
end
if attached rq.supported_request_method_names as l_methods and then not l_methods.is_empty then
s.append_string ("<div class=%"api-method%">Supported request methods: <strong>")
if attached l_methods.new_cursor as l_methods_cursor then
from
until
l_methods_cursor.after
loop
s.append_string (" ")
s.append_string (l_methods_cursor.item)
l_methods_cursor.forth
end
end
s.append_string ("</strong></div>")
end
s.append_string ("<div class=%"api-auth%">Authentication required: <strong>" + rq.authentication_required.out + "</strong></div>")
if attached {REST_REQUEST_URI_TEMPLATE_ROUTER [REST_REQUEST_HANDLER [REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT], REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]} router as l_uri_template_router then
create l_uri_tpl.make (a_resource)
if attached l_uri_tpl.query_variable_names as l_query_variable_names and then not l_query_variable_names.is_empty then
s.append_string ("<div class=%"api-uri-template%">Query parameters: ")
-- s.append_string (rq.path)
from
l_query_variable_names.start
until
l_query_variable_names.after
loop
if l_query_variable_names.isfirst then
s.append_string ("?")
else
s.append_string ("&")
end
if attached l_query_variable_names.item as l_query_param then
s.append_string ("<strong>" + l_query_param + "</strong>")
s.append_string ("=<em>" + l_query_param + "</em>")
end
l_query_variable_names.forth
end
s.append_string ("</div>%N")
end
if attached l_uri_tpl.path_variable_names as l_path_variable_names and then not l_path_variable_names.is_empty then
s.append_string ("<div class=%"api-uri-template%">Path Segment parameters: ")
-- s.append_string (rq.path)
from
l_path_variable_names.start
until
l_path_variable_names.after
loop
if attached l_path_variable_names.item as l_seg_param then
s.append_string ("<em>{" + l_seg_param + "}</em>")
end
l_path_variable_names.forth
end
s.append_string ("</div>%N")
end
end
-- if attached rq._parameters as l_uri_params and then not l_uri_params.is_empty then
-- s.append_string ("<div class=%"api-uri-template%">URI Template: ")
-- s.append_string (rq.path)
-- if attached l_uri_params.new_cursor as l_uri_params_cursor then
-- from
-- until
-- l_uri_params_cursor.after
-- loop
-- if attached l_uri_params_cursor.item as l_uri_param then
-- s.append_string ("/<strong>" + l_uri_param.name + "</strong>")
-- s.append_string ("/<em>{" + l_uri_param.name + "}</em>")
-- end
-- l_uri_params_cursor.forth
-- end
-- end
-- s.append_string ("</div>%N")
-- end
-- if attached rq.parameters as l_params and then not l_params.is_empty then
-- s.append_string ("<div class=%"api-params%">Parameters: ")
-- --| show form only if we have a default format
-- if l_dft_format_name = Void then
-- s.append_string ("<span class=%"note%">to test the parameter(s), please first select a supported format.</span>%N")
-- else
-- if rq.method_post_supported then
-- s.append_string ("<form id=%""+ rq.path +"%" method=%"POST%" action=%"" + ctx.script_url (rq.path) + "." + l_dft_format_name + "%">%N")
-- else
-- s.append_string ("<form id=%""+ rq.path +"%" method=%"GET%" action=%"" + ctx.script_url (rq.path) + "." + l_dft_format_name + "%">%N")
-- end
-- end
-- s.append_string ("<ul>")
-- if attached l_params.new_cursor as l_params_cursor then
-- from
-- until
-- l_params_cursor.after
-- loop
-- if attached l_params_cursor.item as l_param then
-- s.append_string ("<li><strong>" + l_param.name + "</strong>")
-- if l_param.optional then
-- s.append_string (" <em>(Optional)</em>")
-- end
-- if attached l_param.description as l_param_desc then
-- s.append_string (": <em>" + l_param_desc + "</em>")
-- end
-- if l_dft_format_name /= Void then
-- s.append (" <input name=%"" + l_param.name + "%" type=%"text%" />")
-- end
-- s.append_string ("</li>")
-- end
-- l_params_cursor.forth
-- end
-- end
-- if l_dft_format_name /= Void then
-- s.append_string ("<input type=%"submit%" value=%"Test "+ rq.path + "." + l_dft_format_name + "%"/>")
-- s.append_string ("</form>")
-- end
-- s.append_string ("</ul></div>")
-- else
-- if l_dft_format_name /= Void then
-- s.append_string ("<a class=%"api-name%" href=%"" + ctx.script_url (a_resource + "." + l_dft_format_name) + "%">Test "+ a_resource + "." + l_dft_format_name + "</a>")
-- else
-- s.append_string ("<a class=%"api-name%" href=%"" + ctx.script_url (a_resource) + "%">Test "+ a_resource +"</a>")
-- end
-- end
s.append_string ("</div>%N")
-- end
s.append_string ("</div>%N") -- inner
end
feature -- Access
new_html_page: REST_API_DOCUMENTATION_HTML_PAGE
do
create Result.make ("API Documentation")
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,124 @@
note
description: "Summary description for {REST_API_DOCUMENTATION_HTML_PAGE}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REST_API_DOCUMENTATION_HTML_PAGE
inherit
HTML_PAGE
redefine
head,
initialize,
recycle,
compute
end
create
make
feature {NONE} -- Initialization
initialize
do
Precursor
create {LINKED_LIST [like shortcuts.item]} shortcuts.make
end
feature -- Recyle
recycle
do
Precursor
shortcuts.wipe_out
end
feature -- Access
head: REST_API_DOCUMENTATION_HTML_PAGE_HEAD
big_title: detachable STRING
shortcuts: LIST [TUPLE [name: STRING; anchor: STRING]]
add_shortcut (s: STRING)
local
t: STRING
do
t := s.string
t.replace_substring_all ("/", "-")
shortcuts.force ([s,t])
end
last_added_shortcut: STRING
do
if shortcuts.count > 0 and then attached shortcuts.last as sht then
Result := sht.anchor
else
create Result.make_empty
end
end
shortcuts_to_html: detachable STRING
do
if not shortcuts.is_empty then
from
create Result.make_from_string ("<strong>Shortcuts:</strong> | ")
shortcuts.start
until
shortcuts.after
loop
Result.append_string ("<a href=%"#"+ shortcuts.item.anchor +"%">"+ shortcuts.item.name +"</a> | ")
shortcuts.forth
end
end
end
feature -- Element change
set_big_title (t: like big_title)
do
big_title := t
end
feature -- Basic operation
compute
local
l_old_body: STRING
sh, bt: detachable STRING
do
sh := shortcuts_to_html
bt := big_title
if sh /= Void or bt /= Void then
l_old_body := body
if bt /= Void then
bt := "<h1>" + bt + "</h2>%N"
if sh /= Void then
body := bt + sh + l_old_body
else
body := bt + l_old_body
end
elseif sh /= Void then
body := sh + l_old_body
end
Precursor
body := l_old_body
else
Precursor
end
end
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,75 @@
note
description: "Summary description for {REST_API_DOCUMENTATION_HTML_PAGE_HEAD}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REST_API_DOCUMENTATION_HTML_PAGE_HEAD
inherit
HTML_PAGE_HEAD
redefine
initialize,
compute
end
create
make
feature {NONE} -- Initialization
initialize
do
Precursor
style := "[
body {margin: 0px;}
a { text-decoration: none; }
h1 { padding: 10px; border: solid 2px #000; background-color: #009; color: #fff;}
div.api { padding: 5px; margin-bottom: 10px;}
div.api .api-description { padding: 5px 5px 5px 0px; font-style: italic; color: #090;}
div.api div.inner { padding-left: 40px;}
div.api h2>a { color: #009; text-decoration: none;}
div.api a.api-format { color: #009; text-decoration: none;}
div.api a.api-format.selected { padding: 0 4px 0 4px; color: #009; text-decoration: none; border: solid 1px #99c; background-color: #eeeeff;}
div.api>h2 { margin: 2px; padding: 2px 2px 2px 10px; display: inline-block; border: dotted 1px #cce; width: 100%; color: #009; background-color: #E7F3F8; text-decoration: none; font-weight: bold; font-size: 120%;}
div.api span.note { font-style: italic;}
]"
end
feature {REST_API_DOCUMENTATION_HTML_PAGE} -- Access
style: STRING
feature -- Output
compute
-- Compute the string output
local
s: detachable STRING
p: INTEGER
do
Precursor
s := internal_string
if s /= Void then
p := s.substring_index ("</head>", 1)
if p > 0 then
s.insert_string ("<style>%N" + style + "%N</style>%N", p)
else
s.append_string ("<style>%N" + style + "%N</style>%N")
end
end
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,23 @@
note
description: "Summary description for {DEFAULT_REST_APPLICATION}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
DEFAULT_REST_APPLICATION
inherit
REST_APPLICATION [REST_REQUEST_HANDLER [REST_REQUEST_HANDLER_CONTEXT], REST_REQUEST_HANDLER_CONTEXT]
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,24 @@
note
description: "Summary description for {DEFAULT_REST_REQUEST_URI_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
DEFAULT_REST_REQUEST_URI_HANDLER
inherit
REST_REQUEST_HANDLER [REST_REQUEST_URI_HANDLER_CONTEXT]
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,40 @@
note
description: "Summary description for {DEFAULT_REST_REQUEST_URI_ROUTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
DEFAULT_REST_REQUEST_URI_ROUTER
inherit
REST_REQUEST_URI_ROUTER [REST_REQUEST_HANDLER [REST_REQUEST_URI_HANDLER_CONTEXT], REST_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: REST_REQUEST_URI_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER]];
rqst_methods: detachable ARRAY [READABLE_STRING_8])
local
h: REST_REQUEST_AGENT_HANDLER [REST_REQUEST_URI_HANDLER_CONTEXT]
do
create h.make (a_action)
map_with_request_methods (a_id, h, rqst_methods)
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,24 @@
note
description: "Summary description for {DEFAULT_REST_REQUEST_URI_TEMPLATE_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
DEFAULT_REST_REQUEST_URI_TEMPLATE_HANDLER
inherit
REST_REQUEST_HANDLER [REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,40 @@
note
description: "Summary description for {DEFAULT_REST_REQUEST_URI_TEMPLATE_ROUTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
DEFAULT_REST_REQUEST_URI_TEMPLATE_ROUTER
inherit
REST_REQUEST_URI_TEMPLATE_ROUTER [REST_REQUEST_HANDLER [REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT], REST_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: REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER]];
rqst_methods: detachable ARRAY [READABLE_STRING_8])
local
h: REST_REQUEST_AGENT_HANDLER [REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
do
create h.make (a_action)
map_with_request_methods (a_id, h, rqst_methods)
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,33 @@
note
description: "Summary description for {DEFAULT_REST_REQUEST_URI_TEMPLATE_ROUTING_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
DEFAULT_REST_REQUEST_URI_TEMPLATE_ROUTING_HANDLER
inherit
REST_REQUEST_URI_TEMPLATE_ROUTING_HANDLER [REST_REQUEST_HANDLER [REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT], REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
redefine
router
end
create
make
feature {NONE} -- Routing
router: DEFAULT_REST_REQUEST_URI_TEMPLATE_ROUTER
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,30 @@
note
description: "Summary description for {DEFAULT_REST_URI_APPLICATION}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
DEFAULT_REST_URI_APPLICATION
inherit
REST_APPLICATION [REST_REQUEST_HANDLER [REST_REQUEST_URI_HANDLER_CONTEXT], REST_REQUEST_URI_HANDLER_CONTEXT]
redefine
router
end
feature -- Router
router: DEFAULT_REST_REQUEST_URI_ROUTER
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,30 @@
note
description: "Summary description for {DEFAULT_URI_TEMPLATE_REST_APPLICATION}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
DEFAULT_REST_URI_TEMPLATE_APPLICATION
inherit
REST_APPLICATION [REST_REQUEST_HANDLER [REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT], REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
redefine
router
end
feature -- Router
router: DEFAULT_REST_REQUEST_URI_TEMPLATE_ROUTER
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,56 @@
note
description: "Summary description for REST_REQUEST_AGENT_HANDLER."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REST_REQUEST_AGENT_HANDLER [C -> REST_REQUEST_HANDLER_CONTEXT]
inherit
REQUEST_AGENT_HANDLER [C]
redefine
execute
end
REST_REQUEST_HANDLER [C]
redefine
execute
end
create
make
feature -- status
authentication_required: BOOLEAN
feature -- Element change
set_authentication_required (b: like authentication_required)
do
authentication_required := b
end
feature -- Execution
execute (a_hdl_context: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
Precursor {REST_REQUEST_HANDLER} (a_hdl_context, req, res)
end
-- execute_application (ctx: REST_REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- do
-- action.call ([ctx, req, res])
-- end
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,47 @@
note
description: "Summary description for REST_REQUEST_GROUP_HANDLER."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REST_REQUEST_GROUP_HANDLER [H -> REST_REQUEST_HANDLER [C], C -> REST_REQUEST_HANDLER_CONTEXT]
inherit
REQUEST_ROUTING_HANDLER [H, C]
redefine
execute
end
REST_REQUEST_HANDLER [C]
redefine
execute
select
execute_application
end
create
make
feature -- status
authentication_required: BOOLEAN
feature -- Execution
execute (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
Precursor {REST_REQUEST_HANDLER} (ctx, req, res)
end
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,165 @@
note
description: "Summary description for {REST_REQUEST_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
REST_REQUEST_HANDLER [C -> REST_REQUEST_HANDLER_CONTEXT]
inherit
REQUEST_HANDLER [C]
redefine
execute
end
feature -- Access
authentication_required: BOOLEAN
-- Is authentication required
-- might depend on the request environment
-- or the associated resources
deferred
end
feature -- Execution
execute (a_hdl_context: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- Execute request handler
do
if authentication_required and then not a_hdl_context.authenticated then
execute_unauthorized (a_hdl_context, req, res)
else
Precursor (a_hdl_context, req, res)
end
end
execute_unauthorized (a_hdl_context: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
res.set_status_code ({HTTP_STATUS_CODE}.unauthorized)
res.write_header ({HTTP_STATUS_CODE}.unauthorized, <<["WWW-Authenticate", "Basic realm=%"Eiffel auth%""]>>)
res.write_string ("Unauthorized")
end
feature {NONE} -- Implementation
supported_formats: INTEGER
-- Support request format result such as json, xml, ...
feature {NONE} -- Status report
format_id_supported (a_id: INTEGER): BOOLEAN
do
Result := (supported_formats & a_id) = a_id
end
format_name_supported (n: STRING): BOOLEAN
-- Is format `n' supported?
do
Result := format_id_supported (format_constants.format_id (n))
end
format_constants: HTTP_FORMAT_CONSTANTS
once
create Result
end
feature -- Status report
supported_format_names: LIST [STRING]
-- Supported format names ...
do
create {LINKED_LIST [STRING]} Result.make
if format_json_supported then
Result.extend (format_constants.json_name)
end
if format_xml_supported then
Result.extend (format_constants.xml_name)
end
if format_text_supported then
Result.extend (format_constants.text_name)
end
if format_html_supported then
Result.extend (format_constants.html_name)
end
if format_rss_supported then
Result.extend (format_constants.rss_name)
end
if format_atom_supported then
Result.extend (format_constants.atom_name)
end
end
format_json_supported: BOOLEAN
do
Result := format_id_supported ({HTTP_FORMAT_CONSTANTS}.json)
end
format_xml_supported: BOOLEAN
do
Result := format_id_supported ({HTTP_FORMAT_CONSTANTS}.xml)
end
format_text_supported: BOOLEAN
do
Result := format_id_supported ({HTTP_FORMAT_CONSTANTS}.text)
end
format_html_supported: BOOLEAN
do
Result := format_id_supported ({HTTP_FORMAT_CONSTANTS}.html)
end
format_rss_supported: BOOLEAN
do
Result := format_id_supported ({HTTP_FORMAT_CONSTANTS}.rss)
end
format_atom_supported: BOOLEAN
do
Result := format_id_supported ({HTTP_FORMAT_CONSTANTS}.atom)
end
feature -- Element change: formats
reset_supported_formats
do
supported_formats := 0
end
enable_format_json
do
enable_format ({HTTP_FORMAT_CONSTANTS}.json)
end
enable_format_xml
do
enable_format ({HTTP_FORMAT_CONSTANTS}.xml)
end
enable_format_text
do
enable_format ({HTTP_FORMAT_CONSTANTS}.text)
end
enable_format_html
do
enable_format ({HTTP_FORMAT_CONSTANTS}.html)
end
enable_format (f: INTEGER)
do
supported_formats := supported_formats | f
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,51 @@
note
description: "Summary description for {REQUEST_ROUTING_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REST_REQUEST_URI_ROUTING_HANDLER [H -> REST_REQUEST_HANDLER [C],
C -> REST_REQUEST_URI_HANDLER_CONTEXT create make end]
inherit
REQUEST_URI_ROUTING_HANDLER [H, C]
redefine
router,
execute
end
REST_REQUEST_HANDLER [C]
undefine
execute
end
create
make
feature -- Status report
authentication_required: BOOLEAN
feature -- Execution
execute (a_hdl_context: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
Precursor {REST_REQUEST_HANDLER} (a_hdl_context, req, res)
end
feature {NONE} -- Routing
router: REST_REQUEST_URI_ROUTER [H, C]
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,51 @@
note
description: "Summary description for {REQUEST_ROUTING_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REST_REQUEST_URI_TEMPLATE_ROUTING_HANDLER [H -> REST_REQUEST_HANDLER [C],
C -> REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT create make end]
inherit
REQUEST_URI_TEMPLATE_ROUTING_HANDLER [H, C]
redefine
router,
execute
end
REST_REQUEST_HANDLER [C]
undefine
execute
end
create
make
feature -- Status report
authentication_required: BOOLEAN
feature -- Execution
execute (a_hdl_context: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
Precursor {REST_REQUEST_HANDLER} (a_hdl_context, req, res)
end
feature {NONE} -- Routing
router: REST_REQUEST_URI_TEMPLATE_ROUTER [H, C]
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,156 @@
note
description: "Summary description for {REST_RESPONSE}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REST_RESPONSE
create
make
feature {NONE} -- Initialization
make (a_api: like api)
do
api := a_api
initialize
end
initialize
do
create headers.make
end
feature -- Recycle
recycle
do
headers.recycle
end
feature -- Access
headers: EWF_HEADER
api: STRING
-- Associated api query string.
message: detachable STRING_8
-- Associated message to send with the response.
feature -- Element change
set_message (m: like message)
-- Set `message' to `m'
do
message := m
end
append_message (m: attached like message)
-- Append message `m' to current `message' value
-- create `message' is Void
require
m_not_empty: m /= Void and then not m.is_empty
do
if attached message as msg then
msg.append (m)
else
set_message (m.string)
end
end
append_message_file_content (fn: STRING)
-- Append file content from `fn' to the response
--| To use with care.
--| You should avoid using this for big or binary content ...
local
f: RAW_FILE
do
create f.make (fn)
if f.exists and then f.is_readable then
f.open_read
from
until
f.exhausted
loop
f.read_stream (1024)
append_message (f.last_string)
end
f.close
end
end
feature -- Output
update_content_length (a_overwrite: BOOLEAN)
-- Update content length
-- if the header already exists it won't change it,
-- unless `a_overwrite' is set to True
local
hds: like headers
len: INTEGER
do
hds := headers
if a_overwrite or else not hds.has_content_length then
if attached message as m then
len := m.count
-- if {PLATFORM}.is_windows then
-- len := len + m.occurrences ('%N') --| FIXME: find better solution
-- end
else
len := 0
end
hds.put_content_length (len)
end
end
compute
-- Compute the string output
do
update_content_length (False)
internal_headers_string := headers.string
end
headers_string: STRING
local
o: like internal_headers_string
do
o := internal_headers_string
if o = Void then
compute
o := internal_headers_string
if o = Void then
check output_computed: False end
create o.make_empty
end
end
Result := o
end
send (res: WGI_RESPONSE_BUFFER)
do
compute
res.set_status_code (200)
res.write_headers_string (headers_string)
if attached message as m then
res.write_string (m)
end
end
feature {NONE} -- Implementation: output
internal_headers_string: detachable like headers_string
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,23 @@
note
description: "Summary description for {REST_REQUEST_ROUTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
REST_REQUEST_ROUTER [H -> REST_REQUEST_HANDLER [C], C -> REST_REQUEST_HANDLER_CONTEXT]
inherit
REQUEST_ROUTER [H, C]
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,36 @@
note
description: "Summary description for {REST_REQUEST_URI_ROUTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REST_REQUEST_URI_ROUTER [H -> REST_REQUEST_HANDLER [C], C -> REST_REQUEST_URI_HANDLER_CONTEXT create make end]
inherit
REQUEST_URI_ROUTER [H, C]
REST_REQUEST_ROUTER [H, C]
create
make
feature -- Mapping
-- map_agent_with_request_methods (a_id: READABLE_STRING_8; a_action: like {REST_REQUEST_AGENT_HANDLER}.action; rqst_methods: detachable ARRAY [READABLE_STRING_8])
-- do
-- Precursor (a_id, a_action, rqst_methods)
-- end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,28 @@
note
description: "Summary description for {REST_REQUEST_URI_TEMPLATE_ROUTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REST_REQUEST_URI_TEMPLATE_ROUTER [H -> REST_REQUEST_HANDLER [C], C -> REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT create make end]
inherit
REQUEST_URI_TEMPLATE_ROUTER [H, C]
REST_REQUEST_ROUTER [H, C]
create
make
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,26 @@
Options Indexes FollowSymLinks ExecCGI
<IfModule mod_fcgid.c>
AddHandler fcgid-script .eapp
FcgidWrapper c:/_dev/Dev-Process/web-library/library/server/rest/tests/htdocs/../EIFGENs/rest_sample_cgi/W_code/sample.exe .eapp
</IfModule>
Options +ExecCGI
AddHandler cgi-script exe
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /REST
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ sample.eapp/$1
# To let CGI app knows about HTTP_AUTHORIZATION
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
</IfModule>

View File

@@ -0,0 +1,31 @@
= How to make this works with Apache =
* In the apache's configuration file, be sure to add the following, or similar
LoadModule fcgid_module modules/mod_fcgid.so
<IfModule mod_fcgid.c>
FcgidIdleTimeout 60
FcgidBusyScanInterval 120
FcgidProcessLifeTime 1600
#7200
FcgidMaxProcesses 5
FcgidMaxProcessesPerClass 100
FcgidMinProcessesPerClass 100
FcgidConnectTimeout 8
FcgidIOTimeout 3000
FcgidBusyTimeout 3200
FcgidMaxRequestLen 10000000
FcgidPassHeader Authorization
</IfModule>
alias /REST/ "c:/_dev/Dev-Process/src/server/htdocs/"
<directory "c:/_dev/Dev-Process/src/server/htdocs/">
AllowOverride All
Order allow,deny
Allow from all
</directory>
* You can use <Location>, but then the AllowOverride All, should be somewhere else, since <Location> does not allow it.
* And then, check the .htaccess from this folder for additional settings (but required)

View File

@@ -0,0 +1,24 @@
<?xml version="1.0"?>
<project name="update_htdocs" default="menu">
<target name="menu">
<echo message="1 - update_cgi_link" />
<echo message="2 - update_htaccess" />
<echo message="0 - quit" />
<input message="Your choice :" variable="answer" defaultvalue="0" validargs="1,2,0" answer_required="true" />
<geant target="update_cgi_link" if="$answer=1" />
<geant target="update_htaccess" if="$answer=2" />
</target>
<target name="update_cgi_link">
<echo message="Update CGI link" />
<exec executable="junction CGI ..\EIFGENs\rest_sample_cgi\W_code" accept_errors="true" exit_code_variable="return_code" dir="$cwd" />
</target>
<target name="update_htaccess">
<replace file="htaccess" to_file=".htaccess" token="##SAMPLE-CGI-DIR##" replace="$cwd/../EIFGENs/rest_sample_cgi/W_code" flags="g" />
<replace file=".htaccess" to_file=".htaccess" token="\" replace="/" flags="g" />
</target>
</project>

View File

@@ -0,0 +1,26 @@
Options Indexes FollowSymLinks ExecCGI
<IfModule mod_fcgid.c>
AddHandler fcgid-script .eapp
FcgidWrapper ##SAMPLE-CGI-DIR##/sample.exe .eapp
</IfModule>
Options +ExecCGI
AddHandler cgi-script exe
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /REST
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ sample.eapp/$1
# To let CGI app knows about HTTP_AUTHORIZATION
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
</IfModule>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="common" uuid="4E02DF53-9DF0-44B6-B8A8-7554E0B94957">
<target name="common">
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option cat_call_detection="false" is_attached_by_default="true" void_safety="all" syntax="provisional">
</option>
<setting name="console_application" value="true"/>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="rest" location="..\rest-safe.ecf" readonly="false"/>
<library name="ewsgi" location="..\..\..\ewsgi\ewsgi-safe.ecf" readonly="false"/>
<library name="http" location="..\..\..\..\protocol\http\http-safe.ecf" readonly="false"/>
</target>
<target name="sample_fcgi" extends="common">
<root class="APP_SERVER" feature="make"/>
<setting name="executable_name" value="sample"/>
<library name="ewsgi_fcgi" location="..\..\..\ewsgi/connectors\fcgi\fcgi-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\" recursive="true">
<file_rule>
<exclude>/gateway$</exclude>
</file_rule>
</cluster>
<cluster name="gateway" location=".\src\gateway\fcgi\" recursive="true"/>
</target>
<target name="sample_cgi" extends="common">
<root class="APP_SERVER" feature="make"/>
<setting name="executable_name" value="sample"/>
<library name="ewsgi_cgi" location="..\..\..\ewsgi/connectors\cgi\cgi-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\" recursive="true">
<file_rule>
<exclude>/gateway$</exclude>
</file_rule>
</cluster>
<cluster name="gateway" location=".\src\gateway\cgi\" recursive="true"/>
</target>
<target name="sample_nino" extends="common">
<root class="APP_SERVER" feature="make"/>
<setting name="executable_name" value="sample"/>
<library name="ewsgi_nino" location="..\..\..\ewsgi/connectors\nino\nino-safe.ecf" readonly="false"/>
<library name="ewsgi_default_nino" location="..\..\..\ewsgi/default\ewsgi_nino-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\" recursive="true">
<file_rule>
<exclude>/gateway$</exclude>
</file_rule>
</cluster>
<cluster name="gateway" location=".\src\gateway\nino\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,109 @@
note
description: "Summary description for {APP_ACCOUNT_VERIFY_CREDENTIAL}."
date: "$Date$"
revision: "$Revision$"
class
APP_ACCOUNT_VERIFY_CREDENTIAL
inherit
APP_REQUEST_HANDLER
redefine
initialize,
execute_unauthorized
end
create
make
feature {NONE} -- Initialization
make
do
description := "Verify credentials"
initialize
end
initialize
do
Precursor
enable_request_method_get
enable_format_json
enable_format_xml
enable_format_text
end
feature -- Access
authentication_required: BOOLEAN = True
feature -- Execution
execute_unauthorized (a_hdl_context: APP_REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
local
s: STRING
lst: LIST [STRING]
do
res.set_status_code ({HTTP_STATUS_CODE}.unauthorized)
res.write_header ({HTTP_STATUS_CODE}.unauthorized, <<["WWW-Authenticate", "Basic realm=%"My Silly demo auth, password must be the same as login such as foo:foo%""]>>)
res.write_string ("Unauthorized")
end
execute_application (ctx: APP_REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
local
l_full: BOOLEAN
h: EWF_HEADER
l_login: STRING_8
s: STRING
content_type_supported: ARRAY [STRING]
l_format_id: INTEGER
do
content_type_supported := <<{HTTP_CONSTANTS}.json_app, {HTTP_CONSTANTS}.xml_text, {HTTP_CONSTANTS}.plain_text>>
l_format_id := ctx.request_format_id ("format", content_type_supported)
if ctx.authenticated then
l_full := attached ctx.query_parameter ("details") as v and then v.is_case_insensitive_equal ("true")
if attached ctx.authenticated_identifier as log then
l_login := log.as_string_8
create h.make
create s.make_empty
inspect l_format_id
when {HTTP_FORMAT_CONSTANTS}.json then
h.put_content_type_text_plain
s.append_string ("{ %"login%": %"" + l_login + "%" }%N")
when {HTTP_FORMAT_CONSTANTS}.xml then
h.put_content_type_text_xml
s.append_string ("<login>" + l_login + "</login>%N")
when {HTTP_FORMAT_CONSTANTS}.text then -- Default
h.put_content_type_text_plain
s.append_string ("login: " + l_login + "%N")
else
execute_content_type_not_allowed (req, res, content_type_supported,
<<{HTTP_FORMAT_CONSTANTS}.json_name, {HTTP_FORMAT_CONSTANTS}.html_name, {HTTP_FORMAT_CONSTANTS}.xml_name, {HTTP_FORMAT_CONSTANTS}.text_name>>
)
end
if not s.is_empty then
res.set_status_code ({HTTP_STATUS_CODE}.ok)
res.write_headers_string (h.string)
res.write_string (s)
end
else
send_error (ctx.path, 0, "User/password unknown", Void, ctx, req, res)
end
else
send_error (ctx.path, 0, "Authentication rejected", Void, ctx, req, res)
end
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,95 @@
note
description: "Summary description for {APP_TEST}."
date: "$Date$"
revision: "$Revision$"
class
APP_TEST
inherit
APP_REQUEST_HANDLER
redefine
initialize
end
create
make
feature {NONE} -- Initialization
make
do
description := "Return a simple test output "
initialize
end
initialize
do
Precursor
enable_request_method_get
enable_format_text
end
feature -- Access
authentication_required: BOOLEAN = False
feature -- Execution
execute_application (ctx: APP_REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- Execute request handler
local
s: STRING
h: EWF_HEADER
do
create h.make
h.put_content_type_text_plain
create s.make_empty
s.append_string ("test")
if attached req.meta_variable ("REQUEST_COUNT") as l_request_count then
s.append_string ("(request_count="+ l_request_count +")%N")
end
-- ctx.request_format_id ("format", Void)
if attached ctx.request_format ("format", Void) as l_format then
s.append_string (" format=" + l_format + "%N")
end
if attached ctx.parameter ("op") as l_op then
s.append_string (" op=" + l_op)
if l_op.same_string ("crash") then
(create {DEVELOPER_EXCEPTION}).raise
elseif l_op.starts_with ("env") then
s.append_string ("%N%NAll variables:")
s.append (string_hash_table_string_string (req.parameters.new_cursor, False))
s.append_string ("<br/>script_url(%"" + req.path_info + "%")=" + ctx.script_url (req.path_info) + "%N")
-- if attached ctx.http_authorization_login_password as t then
-- s.append_string ("Check login=" + t.login + "<br/>%N")
-- end
if ctx.authenticated and then attached ctx.authenticated_identifier as l_login then
s.append_string ("Authenticated: login=" + l_login.as_string_8 + "<br/>%N")
end
end
else
s.append ("%N Try " + ctx.script_absolute_url (req.path_info + "?op=env") + " to display all variables%N")
s.append ("%N Try " + ctx.script_absolute_url (req.path_info + "?op=crash") + " to demonstrate exception trace%N")
end
res.set_status_code (200)
res.write_headers_string (h.string)
res.write_string (s)
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,148 @@
note
description: "Summary description for {APP_SERVER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
APP_SERVER
inherit
APP_APPLICATION
redefine
execute
end
REST_APPLICATION_GATEWAY
create
make
feature {NONE} -- Initialization
make
-- Initialize `Current'.
do
initialize_router
build_gateway_and_launch
end
feature {NONE} -- Handlers
create_router
-- Create `router'
do
create router.make (5)
end
setup_router
local
h: APP_REQUEST_HANDLER
rah: APP_REQUEST_AGENT_HANDLER
gh: APP_REQUEST_ROUTING_HANDLER
do
create {APP_ACCOUNT_VERIFY_CREDENTIAL} h.make
router.map ("/account/verify_credentials", h)
router.map ("/account/verify_credentials.{format}", h)
create {APP_TEST} h.make
create gh.make (4)
router.map ("/test", gh)
gh.map_default (h)
-- gh.map ("/test", h)
gh.map ("/test/{op}", h)
gh.map ("/test.{format}", h)
gh.map ("/test.{format}/{op}", h)
create rah.make (agent execute_exit_application)
h := rah
h.set_description ("tell the REST server to exit (in FCGI context, this is used to reload the FCGI server)")
h.enable_request_method_get
h.enable_format_text
router.map ("/debug/exit", h)
router.map ("/debug/exit.{format}", h)
end
feature -- Execution
execute (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
request_count := request_count + 1
Precursor (req, res)
end
execute_default (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
local
rqst_uri: detachable STRING
l_path_info: detachable STRING
h: EWF_HEADER
s: STRING
l_redir_url: STRING
do
create h.make
-- h.put_refresh (ctx.script_url ("/doc"), 2, {HTTP_STATUS_CODE}.temp_redirect)
l_redir_url := "/doc"
h.put_refresh (l_redir_url, 2, {HTTP_STATUS_CODE}.temp_redirect)
h.put_content_type_text_html
create s.make_empty
s := "Request [" + req.path_info + "] is not available. <br/>%N";
s.append ("You are being redirected to <a href=%"" + l_redir_url + "%">/doc</a> in 2 seconds ...%N")
h.put_content_length (s.count)
res.set_status_code (200)
res.write_headers_string (h.string)
res.write_string (s)
end
-- execute_rescue (ctx: like new_request_context)
-- -- Execute the default rescue behavior
-- do
-- execute_exception_trace (ctx)
-- end
feature -- Implementation
-- execute_exception_trace (ctx: like new_request_context)
-- local
-- h: EWF_HEADER
-- s: STRING
-- do
-- create h.make
-- h.put_content_type_text_plain
-- ctx.output.put_string (h.string)
-- ctx.output.put_string ("Error occurred .. rq="+ request_count.out +"%N")
-- if attached (create {EXCEPTIONS}).exception_trace as l_trace then
-- ctx.output.put_string ("<pre>" + l_trace + "</pre>")
-- end
-- h.recycle
-- exit_with_code (-1)
-- end
execute_exit_application (ctx: APP_REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
local
s: STRING
do
res.set_status_code (200)
res.write_header (200, <<["Content-Type", "text/html"]>>)
create s.make_empty
s.append_string ("Exited")
s.append_string (" <a href=%"" + ctx.script_url ("/") + "%">start again</a>%N")
res.write_string (s)
exit_with_code (0)
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,24 @@
deferred class
REST_APPLICATION_GATEWAY
inherit
WGI_APPLICATION
feature -- Access
build_gateway_and_launch
local
cgi: EWF_CGI_CONNECTOR
do
create cgi.make (Current)
cgi.launch
end
gateway_name: STRING = "CGI"
exit_with_code (a_code: INTEGER)
do
(create {EXCEPTIONS}).die (a_code)
end
end

View File

@@ -0,0 +1,35 @@
deferred class
REST_APPLICATION_GATEWAY
inherit
WGI_APPLICATION
feature -- Access
build_gateway_and_launch
local
libfcgi: EWF_LIBFCGI_CONNECTOR
do
create libfcgi.make (Current)
libfcgi.launch
end
gateway_name: STRING = "libFCGI"
exit_with_code (a_code: INTEGER)
do
(create {EXCEPTIONS}).die (a_code)
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,44 @@
deferred class
REST_APPLICATION_GATEWAY
inherit
WGI_APPLICATION
feature -- Access
build_gateway_and_launch
local
app: NINO_APPLICATION
port_number: INTEGER
base_url: STRING
do
port_number := 8080
base_url := ""
debug ("nino")
print ("Example: start a Nino web server on port " + port_number.out +
", %Nand reply Hello World for any request such as http://localhost:" + port_number.out + "/" + base_url + "%N")
end
create app.make_custom (agent execute, base_url)
app.force_single_threaded
app.listen (port_number)
end
gateway_name: STRING = "NINO"
exit_with_code (a_code: INTEGER)
do
(create {EXCEPTIONS}).die (a_code)
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,30 @@
note
description: "Summary description for {APP_APPLICATION}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
APP_APPLICATION
inherit
REST_APPLICATION [APP_REQUEST_HANDLER, APP_REQUEST_HANDLER_CONTEXT]
redefine
router
end
feature {NONE} -- Router
router: APP_REQUEST_ROUTER
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,32 @@
note
description: "Summary description for REST_REQUEST_AGENT_HANDLER."
author: ""
date: "$Date$"
revision: "$Revision$"
class
APP_REQUEST_AGENT_HANDLER
inherit
APP_REQUEST_HANDLER
undefine
execute, pre_execute, post_execute
end
REST_REQUEST_AGENT_HANDLER [APP_REQUEST_HANDLER_CONTEXT]
create
make
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,60 @@
note
description : "Objects that ..."
date : "$Date$"
revision : "$Revision$"
deferred class
APP_REQUEST_HANDLER
inherit
REST_REQUEST_HANDLER [APP_REQUEST_HANDLER_CONTEXT]
APP_REQUEST_HELPER
feature {NONE} -- Implementation
string_hash_table_string_string (ht: HASH_TABLE_ITERATION_CURSOR [READABLE_STRING_GENERAL, READABLE_STRING_GENERAL]; using_pre: BOOLEAN): STRING_8
do
create Result.make (100)
if using_pre then
Result.append ("<pre>")
end
from
ht.start
until
ht.after
loop
Result.append_string (ht.key.as_string_8 + " = " + ht.item.as_string_8 + "%N")
ht.forth
end
if using_pre then
Result.append ("</pre>")
end
end
feature -- Helpers
format_id (s: detachable STRING): INTEGER
do
Result := {HTTP_FORMAT_CONSTANTS}.text
if s /= Void then
Result := format_constants.format_id (s)
end
end
exit_with_code (a_code: INTEGER)
do
(create {EXCEPTIONS}).die (a_code)
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,86 @@
note
description : "Objects that ..."
author : "$Author$"
date : "$Date$"
revision : "$Revision$"
class
APP_REQUEST_HANDLER_CONTEXT
inherit
REST_REQUEST_URI_TEMPLATE_HANDLER_CONTEXT
redefine
authenticated,
authenticated_identifier
end
create
make
feature -- Auth
authenticated: BOOLEAN
do
if attached request.http_authorization as l_http_auth then
Result := True
end
end
authenticated_identifier: detachable READABLE_STRING_32
do
if authenticated then
Result := "foo"
end
end
feature -- Format
get_format_id (a_format_variable_name: detachable READABLE_STRING_GENERAL; a_content_type_supported: detachable ARRAY [STRING_8])
do
if internal_format_id = 0 then
internal_format_id := request_format_id (a_format_variable_name, a_content_type_supported)
end
end
get_format_name (a_format_variable_name: detachable READABLE_STRING_GENERAL; a_content_type_supported: detachable ARRAY [STRING_8])
do
if internal_format_name = Void then
internal_format_name := request_format (a_format_variable_name, a_content_type_supported)
end
end
format_id: INTEGER
do
if internal_format_id = 0 then
get_format_id (Void, Void)
end
Result := internal_format_id
end
format_name: detachable READABLE_STRING_8
do
Result := internal_format_name
if Result = Void then
Result := request_format (Void, Void)
internal_format_name := Result
end
end
feature {NONE} -- Internal
internal_format_id: like format_id
internal_format_name: like format_name
;note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,101 @@
note
description: "Summary description for {APP_REQUEST_HELPER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
APP_REQUEST_HELPER
feature -- Helpers
send_error (a_path: STRING; a_error_id: INTEGER; a_error_name: STRING; a_error_message: detachable STRING; ctx: APP_REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
local
s: STRING
i,nb: INTEGER
rep_data: REST_RESPONSE
do
res.set_status_code ({HTTP_STATUS_CODE}.expectation_failed)
create rep_data.make (a_path)
rep_data.headers.put_content_type_text_plain
create s.make_empty
inspect ctx.format_id
when {HTTP_FORMAT_CONSTANTS}.json then
rep_data.headers.put_content_type_text_plain
s := "{%"application%": %"" + a_path + "%""
s.append_string (", %"error%": {")
s.append_string ("%"id%": " + a_error_id.out)
s.append_string (",%"name%": %"" + a_error_name + "%"")
if a_error_message /= Void then
s.append_string (",%"message%": %"")
if a_error_message.has ('%N') then
from
i := s.count
s.append_string (a_error_message)
nb := s.count
until
i > nb
loop
inspect s[i]
when '%R' then
if s.valid_index (i+1) and then s[i+1] = '%N' then
s[i] := '\'
s[i+1] := 'n'
i := i + 1
end
when '%N' then
s.insert_character ('\', i)
s[i] := 'n'
else
end
i := i + 1
end
else
s.append_string (a_error_message)
end
s.append_string ("%"")
end
s.append_string ("}") -- end error
s.append_string ("}") -- end global object
rep_data.set_message (s)
when {HTTP_FORMAT_CONSTANTS}.xml then
rep_data.headers.put_content_type_text_xml
s := "<application path=%"" + a_path + "%"><error id=%"" + a_error_id.out + "%" name=%""+ a_error_name +"%">"
if a_error_message /= Void then
s.append_string (a_error_message)
end
s.append_string ("</error></application>")
rep_data.set_message (s)
when {HTTP_FORMAT_CONSTANTS}.html then
rep_data.headers.put_content_type_text_html
s := "<strong>application</strong>: " + a_path + "<br/>%N<strong>Error</strong> (" + a_error_id.out + ") %"" + a_error_name + "%"<br/>%N"
if a_error_message /= Void then
s.append_string ("<blockquote>" + a_error_message + "</blockquote>")
end
rep_data.set_message (s)
when {HTTP_FORMAT_CONSTANTS}.text then -- Default
s := "Application: " + a_path + "<br/>%N"
s.append_string ("Error (" + a_error_id.out + ") %"" + a_error_name + "%"%N")
if a_error_message /= Void then
s.append_string ("%T" + a_error_message + "%N")
end
rep_data.set_message (s)
end
rep_data.send (res)
rep_data.recycle
end
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,26 @@
note
description: "Summary description for {REST_REQUEST_URI_TEMPLATE_ROUTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
APP_REQUEST_ROUTER
inherit
REST_REQUEST_URI_TEMPLATE_ROUTER [APP_REQUEST_HANDLER, APP_REQUEST_HANDLER_CONTEXT]
create
make
note
copyright: "Copyright (c) 1984-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

View File

@@ -0,0 +1,40 @@
note
description: "Summary description for {APP_REQUEST_ROUTING_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
APP_REQUEST_ROUTING_HANDLER
inherit
APP_REQUEST_HANDLER
undefine
execute,
pre_execute,
post_execute
end
REST_REQUEST_URI_TEMPLATE_ROUTING_HANDLER [APP_REQUEST_HANDLER, APP_REQUEST_HANDLER_CONTEXT]
redefine
router
end
create
make
feature {NONE} -- Routing
router: APP_REQUEST_ROUTER
;note
copyright: "Copyright (c) 1984-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

View File

@@ -7,7 +7,7 @@
<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"/>
@@ -15,7 +15,6 @@
<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"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
<target name="ewsgi_compliant_router">
@@ -33,7 +32,6 @@
<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"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</target>
</system>

View File

@@ -7,7 +7,7 @@
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true">
<option warning="true" full_class_checking="true" syntax="provisional">
<assertions precondition="true"/>
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
@@ -15,7 +15,6 @@
<library name="ewsgi" location="..\..\ewsgi\ewsgi.ecf"/>
<library name="http" location="..\..\..\protocol\http\http.ecf"/>
<library name="uri_template" location="..\..\..\protocol\uri_template\uri_template.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
<target name="ewsgi_compliant_router">
@@ -33,7 +32,6 @@
<library name="ewsgi" location="..\..\ewsgi\ewsgi.ecf"/>
<library name="http" location="..\..\..\protocol\http\http.ecf"/>
<library name="uri_template" location="..\..\..\protocol\uri_template\uri_template.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -5,7 +5,7 @@ note
revision: "$Revision$"
deferred class
ROUTED_APPLICATION
ROUTED_APPLICATION [H -> REQUEST_HANDLER [C], C -> REQUEST_HANDLER_CONTEXT]
feature -- Setup
@@ -30,16 +30,17 @@ feature -- Setup
deferred
end
router: REQUEST_ROUTER
router: REQUEST_ROUTER [H, C]
-- Request router
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

@@ -20,10 +20,57 @@ feature -- Access
request: WGI_REQUEST
-- Associated request
path: READABLE_STRING_GENERAL
path: READABLE_STRING_8
-- Associated path
request_content_type (content_type_supported: detachable ARRAY [STRING]): detachable READABLE_STRING_8
feature {NONE} -- Constants
Format_constants: HTTP_FORMAT_CONSTANTS
once
create Result
end
feature -- Query
request_format (a_format_variable_name: detachable READABLE_STRING_GENERAL; 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 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_GENERAL; 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_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 [READABLE_STRING_8]): detachable READABLE_STRING_8
local
s: detachable READABLE_STRING_32
i,n: INTEGER

View File

@@ -0,0 +1,39 @@
note
description: "Summary description for {DEFAULT_REQUEST_URI_ROUTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
DEFAULT_REQUEST_URI_ROUTER
inherit
REQUEST_URI_ROUTER [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: WGI_REQUEST; res: WGI_RESPONSE_BUFFER]];
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

View File

@@ -0,0 +1,40 @@
note
description: "Summary description for {DEFAULT_REQUEST_URI_TEMPLATE_ROUTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
DEFAULT_REQUEST_URI_TEMPLATE_ROUTER
inherit
REQUEST_URI_TEMPLATE_ROUTER [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: WGI_REQUEST; res: WGI_RESPONSE_BUFFER]];
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

View File

@@ -0,0 +1,33 @@
note
description: "Summary description for {DEFAULT_REQUEST_URI_TEMPLATE_ROUTING_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
DEFAULT_REQUEST_URI_TEMPLATE_ROUTING_HANDLER
inherit
REQUEST_URI_TEMPLATE_ROUTING_HANDLER [REQUEST_HANDLER [REQUEST_URI_TEMPLATE_HANDLER_CONTEXT], REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
redefine
router
end
create
make
feature {NONE} -- Routing
router: DEFAULT_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

View File

@@ -0,0 +1,23 @@
note
description: "Summary description for {DEFAULT_ROUTED_APPLICATION}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
DEFAULT_ROUTED_APPLICATION
inherit
ROUTED_APPLICATION [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

View File

@@ -0,0 +1,30 @@
note
description: "Summary description for {DEFAULT_URI_ROUTED_APPLICATION}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
DEFAULT_URI_ROUTED_APPLICATION
inherit
ROUTED_APPLICATION [REQUEST_HANDLER [REQUEST_URI_HANDLER_CONTEXT], REQUEST_URI_HANDLER_CONTEXT]
redefine
router
end
feature -- Router
router: DEFAULT_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

View File

@@ -0,0 +1,30 @@
note
description: "Summary description for {DEFAULT_URI_TEMPLATE_ROUTED_APPLICATION}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
DEFAULT_URI_TEMPLATE_ROUTED_APPLICATION
inherit
ROUTED_APPLICATION [REQUEST_HANDLER [REQUEST_URI_TEMPLATE_HANDLER_CONTEXT], REQUEST_URI_TEMPLATE_HANDLER_CONTEXT]
redefine
router
end
feature -- Router
router: DEFAULT_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

View File

@@ -5,10 +5,10 @@ note
revision: "$Revision$"
class
REQUEST_AGENT_HANDLER
REQUEST_AGENT_HANDLER [C -> REQUEST_HANDLER_CONTEXT]
inherit
REQUEST_HANDLER
REQUEST_HANDLER [C]
create
make
@@ -23,11 +23,11 @@ feature -- Initialization
feature -- Access
action: PROCEDURE [ANY, TUPLE [ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER]]
action: PROCEDURE [ANY, TUPLE [ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER]]
feature -- Execution
execute_application (ctx: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
execute_application (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
do
action.call ([ctx, req, res])
end

View File

@@ -5,7 +5,15 @@ note
revision: "$Revision$"
deferred class
REQUEST_HANDLER
REQUEST_HANDLER [C -> REQUEST_HANDLER_CONTEXT]
inherit
ANY
ROUTED_APPLICATION_HELPER
export
{NONE} all
end
feature {NONE} -- Initialization
@@ -29,7 +37,7 @@ feature -- Status report
feature -- Execution
execute (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
execute (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- Execute request handler
require
is_valid_context: is_valid_context (req)
@@ -38,139 +46,67 @@ feature -- Execution
do
if not rescued then
if request_method_name_supported (req.request_method) then
pre_execute (req)
execute_application (a_hdl_context, req, res)
post_execute (req, res)
pre_execute (ctx, req, res)
execute_application (ctx, req, res)
post_execute (ctx, 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)
rescue_execute (ctx, req, res)
end
rescue
rescued := True
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_application (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- Execute request handler
deferred
end
pre_execute (req: WGI_REQUEST)
pre_execute (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- Operation processed before `execute'
do
--| To be redefined if needed
end
post_execute (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
post_execute (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- Operation processed after `execute'
do
--| To be redefined if needed
end
rescue_execute (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
rescue_execute (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- Operation processed after a rescue
do
--| To be redefined if needed
post_execute (req, res)
post_execute (ctx, req, res)
end
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

@@ -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_application (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
-- 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: WGI_REQUEST; res: WGI_RESPONSE_BUFFER]])
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: WGI_REQUEST; res: WGI_RESPONSE_BUFFER]];
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

View File

@@ -0,0 +1,38 @@
note
description: "Summary description for {REQUEST_ROUTING_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REQUEST_URI_ROUTING_HANDLER [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 [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

View File

@@ -0,0 +1,38 @@
note
description: "Summary description for {REQUEST_ROUTING_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
REQUEST_URI_TEMPLATE_ROUTING_HANDLER [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 [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

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

@@ -5,54 +5,69 @@ note
revision: "$Revision$"
deferred class
REQUEST_ROUTER
REQUEST_ROUTER [H -> REQUEST_HANDLER [C], C -> REQUEST_HANDLER_CONTEXT]
inherit
ITERABLE [TUPLE [handler: REQUEST_HANDLER; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]]
feature -- Mapping
feature -- Registration
map_default (r: like default_handler)
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
default_handler := r
set_default_handler (h)
end
map (a_id: READABLE_STRING_8; h: REQUEST_HANDLER)
-- Map handler `h' with `a_id'
map (a_resource: READABLE_STRING_8; h: H)
-- Map handler `h' with `a_resource'
do
map_with_request_methods (a_id, h, Void)
map_with_request_methods (a_resource, h, Void)
end
map_with_request_methods (a_id: READABLE_STRING_8; h: REQUEST_HANDLER; rqst_methods: detachable ARRAY [READABLE_STRING_8])
-- Map handler `h' with `a_id' and `rqst_methods'
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_id: READABLE_STRING_8; a_action: like {REQUEST_AGENT_HANDLER}.action)
map_agent (a_resource: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER]])
do
map_agent_with_request_methods (a_id, a_action, Void)
map_agent_with_request_methods (a_resource, a_action, Void)
end
map_agent_with_request_methods (a_id: READABLE_STRING_8; a_action: like {REQUEST_AGENT_HANDLER}.action; rqst_methods: detachable ARRAY [READABLE_STRING_8])
map_agent_with_request_methods (a_resource: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER]];
rqst_methods: detachable ARRAY [READABLE_STRING_8])
local
h: REQUEST_AGENT_HANDLER
rah: REQUEST_AGENT_HANDLER [C]
do
create h.make (a_action)
map_with_request_methods (a_id, h, rqst_methods)
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 -- 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): 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 REQUEST_HANDLER_CONTEXT
ctx: detachable like default_handler_context
do
d := handler (req)
if d /= Void then
@@ -60,21 +75,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: 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
handler (req: WGI_REQUEST): detachable TUPLE [handler: REQUEST_HANDLER; context: REQUEST_HANDLER_CONTEXT]
handler (req: WGI_REQUEST): detachable TUPLE [handler: H; context: like default_handler_context]
-- Handler whose map matched with `req'
require
req_valid: req /= Void and then req.path_info /= Void
@@ -127,8 +150,24 @@ feature {NONE} -- Access: Implementation
feature {NONE} -- Implementation
default_handler: detachable REQUEST_HANDLER
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: WGI_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"

View File

@@ -5,10 +5,10 @@ note
revision: "$Revision$"
class
REQUEST_URI_ROUTER
REQUEST_URI_ROUTER [H -> REQUEST_HANDLER [C], C -> REQUEST_URI_HANDLER_CONTEXT create make end]
inherit
REQUEST_ROUTER
REQUEST_ROUTER [H, C]
create
make
@@ -23,17 +23,17 @@ feature -- Initialization
feature -- Registration
map_with_request_methods (p: READABLE_STRING_8; h: REQUEST_HANDLER; rqst_methods: detachable ARRAY [READABLE_STRING_8])
map_with_request_methods (p: READABLE_STRING_8; h: H; rqst_methods: detachable ARRAY [READABLE_STRING_8])
do
handlers.force ([h, p, formatted_request_methods (rqst_methods)])
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: H; context: like default_handler_context]
local
h: detachable REQUEST_HANDLER
ctx: detachable REQUEST_HANDLER_CONTEXT
h: detachable H
ctx: detachable like default_handler_context
do
h := handler_by_path (req.path_info, req.request_method)
if h = Void then
@@ -54,7 +54,7 @@ feature {NONE} -- Access: Implementation
end
end
smart_handler (req: WGI_REQUEST): detachable TUPLE [path: READABLE_STRING_8; handler: REQUEST_HANDLER]
smart_handler (req: WGI_REQUEST): detachable TUPLE [path: READABLE_STRING_8; handler: H]
require
req_valid: req /= Void and then req.path_info /= Void
do
@@ -63,7 +63,7 @@ feature {NONE} -- Access: Implementation
req_path_info_unchanged: req.path_info.same_string (old req.path_info)
end
handler_by_path (a_path: READABLE_STRING_GENERAL; rqst_method: READABLE_STRING_GENERAL): detachable REQUEST_HANDLER
handler_by_path (a_path: READABLE_STRING_GENERAL; rqst_method: READABLE_STRING_GENERAL): detachable H
require
a_path_valid: a_path /= Void
local
@@ -82,18 +82,17 @@ 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
smart_handler_by_path (a_path: READABLE_STRING_8; rqst_method: READABLE_STRING_GENERAL): detachable TUPLE [path: READABLE_STRING_8; handler: REQUEST_HANDLER]
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 REQUEST_HANDLER
h: detachable H
do
l_context_path := context_path (a_path)
from
@@ -115,20 +114,23 @@ 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): C
local
ctx: C
do
if p /= Void then
create Result.make (req, p)
create ctx.make (req, p)
else
create Result.make (req, req.path_info)
create ctx.make (req, req.path_info)
end
Result := ctx
end
feature -- Access
new_cursor: ITERATION_CURSOR [TUPLE [handler: REQUEST_HANDLER; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]]
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
@@ -136,7 +138,7 @@ feature -- Access
feature {NONE} -- Implementation
handlers: ARRAYED_LIST [TUPLE [handler: REQUEST_HANDLER; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]]
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'
@@ -165,7 +167,21 @@ feature {NONE} -- Implementation
result_not_empty: not Result.is_empty
end
;note
feature {NONE} -- Default: implementation
default_handler: detachable H
set_default_handler (h: like default_handler)
do
default_handler := h
end
default_handler_context (req: WGI_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: "[

View File

@@ -5,10 +5,10 @@ note
revision: "$Revision$"
class
REQUEST_URI_TEMPLATE_ROUTER
REQUEST_URI_TEMPLATE_ROUTER [H -> REQUEST_HANDLER [C], C -> REQUEST_URI_TEMPLATE_HANDLER_CONTEXT create make end]
inherit
REQUEST_ROUTER
REQUEST_ROUTER [H, C]
create
make
@@ -24,18 +24,18 @@ feature -- Initialization
feature -- Registration
map_with_uri_template (uri: URI_TEMPLATE; h: REQUEST_HANDLER)
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: REQUEST_HANDLER; rqst_methods: detachable ARRAY [READABLE_STRING_8])
map_with_uri_template_and_request_methods (uri: URI_TEMPLATE; h: H; rqst_methods: detachable ARRAY [READABLE_STRING_8])
do
handlers.force ([h, uri.template, formatted_request_methods (rqst_methods)])
templates.force (uri, uri.template)
end
map_with_request_methods (tpl: READABLE_STRING_8; h: REQUEST_HANDLER; rqst_methods: detachable ARRAY [READABLE_STRING_8])
map_with_request_methods (tpl: READABLE_STRING_8; h: H; rqst_methods: detachable ARRAY [READABLE_STRING_8])
local
uri: URI_TEMPLATE
do
@@ -45,13 +45,13 @@ 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
l_req_method: READABLE_STRING_GENERAL
l_res: URI_TEMPLATE_MATCH_RESULT
do
p := req.request_uri
from
@@ -64,11 +64,18 @@ feature {NONE} -- Access: Implementation
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 templates.item (t) as tpl and then
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, 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
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,9 +83,9 @@ 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): C
do
if p /= Void then
create Result.make (req, tpl, tpl_res, p)
@@ -89,7 +96,7 @@ feature -- Context factory
feature -- Access: ITERABLE
new_cursor: ITERATION_CURSOR [TUPLE [handler: REQUEST_HANDLER; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]]
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
@@ -97,7 +104,7 @@ feature -- Access: ITERABLE
feature {NONE} -- Implementation
handlers: ARRAYED_LIST [TUPLE [handler: REQUEST_HANDLER; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]]
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'
@@ -126,6 +133,23 @@ feature {NONE} -- Implementation
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: WGI_REQUEST): C
local
tpl: URI_TEMPLATE
do
create tpl.make ("/")
Result := handler_context ("/", req, tpl, 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)"