diff --git a/examples/hello_routed_world/hello-safe.ecf b/examples/hello_routed_world/hello-safe.ecf
new file mode 100644
index 00000000..294b85fe
--- /dev/null
+++ b/examples/hello_routed_world/hello-safe.ecf
@@ -0,0 +1,24 @@
+
+
+
+
+
+ /EIFGENs$
+ /\.git$
+ /\.svn$
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/hello_routed_world/license.lic b/examples/hello_routed_world/license.lic
new file mode 100644
index 00000000..cf2d1ed9
--- /dev/null
+++ b/examples/hello_routed_world/license.lic
@@ -0,0 +1,10 @@
+${NOTE_KEYWORD}
+ copyright: "2011-${YEAR}, Eiffel Software and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+ source: "[
+ Eiffel Software
+ 5949 Hollister Ave., Goleta, CA 93117 USA
+ Telephone 805-685-1006, Fax 805-685-6869
+ Website http://www.eiffel.com
+ Customer support http://support.eiffel.com
+ ]"
diff --git a/examples/hello_routed_world/src/framework/routed_application_helper.e b/examples/hello_routed_world/src/framework/routed_application_helper.e
new file mode 100644
index 00000000..23129e61
--- /dev/null
+++ b/examples/hello_routed_world/src/framework/routed_application_helper.e
@@ -0,0 +1,145 @@
+note
+ description: "Summary description for {ROUTED_APPLICATION_HELPER}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ ROUTED_APPLICATION_HELPER
+
+inherit
+ ANY
+
+ HTTP_FORMAT_CONSTANTS
+ export
+ {NONE} all
+ end
+
+feature -- Helper
+
+ execute_content_type_not_allowed (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM; a_content_types: detachable ARRAY [STRING]; a_uri_formats: detachable ARRAY [STRING])
+ local
+ s, uri_s: detachable STRING
+ i, n: INTEGER
+ h: GW_HEADER
+ do
+ create h.make
+ h.put_status ({HTTP_STATUS_CODE}.unsupported_media_type)
+ h.put_content_type_text_plain
+
+ if a_content_types /= Void then
+ create s.make (10)
+ from
+ i := a_content_types.lower
+ n := a_content_types.upper
+ until
+ i > n
+ loop
+ s.append_string (a_content_types[i])
+ if i < n then
+ s.append_character (',')
+ s.append_character (' ')
+ end
+ i := i + 1
+ end
+ h.put_header_key_value ("Accept", s)
+ end
+ if a_uri_formats /= Void then
+ create uri_s.make (10)
+ from
+ i := a_uri_formats.lower
+ n := a_uri_formats.upper
+ until
+ i > n
+ loop
+ uri_s.append_string (a_uri_formats[i])
+ if i < n then
+ uri_s.append_character (',')
+ uri_s.append_character (' ')
+ end
+ i := i + 1
+ end
+ end
+ if s /= Void then
+ res.write_string ("Unsupported request content-type, Accept: " + s + "%N")
+ end
+ if uri_s /= Void then
+ res.write_string ("Unsupported request format from the URI: " + uri_s + "%N")
+ end
+ end
+
+ execute_method_not_allowed (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM; a_methods: ARRAY [STRING])
+ local
+ s: STRING
+ i, n: INTEGER
+ do
+ create s.make (10)
+ from
+ i := a_methods.lower
+ n := a_methods.upper
+ until
+ i > n
+ loop
+ s.append_string (a_methods[i])
+ if i < n then
+ s.append_character (',')
+ s.append_character (' ')
+ end
+ i := i + 1
+ end
+
+ res.write_header ({HTTP_STATUS_CODE}.method_not_allowed, <<
+ ["Content-Type", {HTTP_CONSTANTS}.plain_text],
+ ["Allow", s]
+ >>)
+ 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, l_content_type: 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 STRING): 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)"
+ source: "[
+ Eiffel Software
+ 5949 Hollister Ave., Goleta, CA 93117 USA
+ Telephone 805-685-1006, Fax 805-685-6869
+ Website http://www.eiffel.com
+ Customer support http://support.eiffel.com
+ ]"
+end
diff --git a/examples/hello_routed_world/src/hello_routed_world.e b/examples/hello_routed_world/src/hello_routed_world.e
new file mode 100644
index 00000000..4dd44baa
--- /dev/null
+++ b/examples/hello_routed_world/src/hello_routed_world.e
@@ -0,0 +1,183 @@
+note
+ description : "Objects that ..."
+ author : "$Author$"
+ date : "$Date$"
+ revision : "$Revision$"
+
+class
+ HELLO_ROUTED_WORLD
+
+inherit
+ ANY
+
+ ROUTED_APPLICATION
+
+ ROUTED_APPLICATION_HELPER
+
+ DEFAULT_EWSGI_APPLICATION
+
+create
+ make
+
+feature {NONE} -- Initialization
+
+ make
+ do
+ initialize_router
+ make_and_launch
+ end
+
+ create_router
+ do
+-- create {REQUEST_URI_ROUTER} router.make (5)
+ create {REQUEST_URI_TEMPLATE_ROUTER} router.make (5)
+ end
+
+ setup_router
+ local
+ ra: REQUEST_AGENT_HANDLER
+ do
+ router.map_agent ("/home", agent execute_home)
+
+ create ra.make (agent handle_hello)
+ 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)
+ end
+
+feature -- Execution
+
+ execute_default (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM)
+ local
+ h: GW_HEADER
+ l_url: STRING
+ e: EXECUTION_ENVIRONMENT
+ n: INTEGER
+ i: INTEGER
+ do
+ create h.make
+ l_url := req.script_url ("/home")
+ n := 3
+ h.put_refresh (l_url, 5, 200)
+ res.set_status_code (200)
+ res.write_string (h.string)
+ from
+ create e
+ until
+ n = 0
+ loop
+ if n > 1 then
+ res.write_string ("Redirected to " + l_url + " in " + n.out + " seconds :%N")
+ else
+ res.write_string ("Redirected to " + l_url + " in 1 second :%N")
+ end
+ res.flush
+ from
+ i := 1
+ until
+ i = 1001
+ loop
+ res.write_string (".")
+ if i \\ 100 = 0 then
+ res.write_string ("%N")
+ end
+ res.flush
+ e.sleep (1_000_000)
+ i := i + 1
+ end
+ res.write_string ("%N")
+ n := n - 1
+ end
+ res.write_string ("You are now being redirected...%N")
+ res.flush
+ end
+
+ execute_home (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM)
+ do
+ res.write_header (200, <<["Content-Type", "text/html"]>>)
+ res.write_string ("
Hello World ?!%N")
+ res.write_string ("Please try the following links
%N")
+
+ if attached req.environment_variable ("REQUEST_COUNT") as rqc then
+ res.write_string ("request #"+ rqc + "%N")
+ end
+ res.write_string ("%N")
+ end
+
+ execute_hello (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM; a_name: detachable STRING_32; ctx: REQUEST_HANDLER_CONTEXT)
+ local
+ l_response_content_type: detachable STRING
+ msg: STRING
+ h: GW_HEADER
+ content_type_supported: ARRAY [STRING]
+ do
+ if a_name /= Void then
+ msg := "Hello %"" + a_name + "%" !%N"
+ else
+ 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)
+ when {HTTP_FORMAT_CONSTANTS}.json then
+ l_response_content_type := {HTTP_CONSTANTS}.json_app
+ msg := "{%N%"application%": %"/hello%",%N %"message%": %"" + msg + "%" %N}"
+ when {HTTP_FORMAT_CONSTANTS}.html then
+ l_response_content_type := {HTTP_CONSTANTS}.html_text
+ when {HTTP_FORMAT_CONSTANTS}.xml then
+ l_response_content_type := {HTTP_CONSTANTS}.xml_text
+ msg := "/hello" + msg + "%N"
+ when {HTTP_FORMAT_CONSTANTS}.text then
+ l_response_content_type := {HTTP_CONSTANTS}.plain_text
+ 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 l_response_content_type /= Void then
+ create h.make
+ h.put_status (200)
+ h.put_content_type (l_response_content_type)
+ h.put_content_length (msg.count)
+ res.set_status_code (200)
+ res.write_string (h.string)
+-- res.write_header (200, <<
+-- ["Content-Type", l_response_content_type],
+-- ["Content-Length", msg.count.out
+-- >>)
+ res.write_string (msg)
+ end
+ end
+
+ handle_hello (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM)
+ do
+ execute_hello (req, res, Void, ctx)
+ end
+
+ handle_anonymous_hello (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM)
+ do
+ execute_hello (req, res, ctx.parameter ("name"), ctx)
+ end
+
+note
+ copyright: "2011-2011, Eiffel Software and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+ source: "[
+ Eiffel Software
+ 5949 Hollister Ave., Goleta, CA 93117 USA
+ Telephone 805-685-1006, Fax 805-685-6869
+ Website http://www.eiffel.com
+ Customer support http://support.eiffel.com
+ ]"
+end
diff --git a/library/server/request/router/router-safe.ecf b/library/server/request/router/router-safe.ecf
index 48d0b68b..5b6a5bf0 100644
--- a/library/server/request/router/router-safe.ecf
+++ b/library/server/request/router/router-safe.ecf
@@ -11,6 +11,7 @@
+
diff --git a/library/server/request/router/router.ecf b/library/server/request/router/router.ecf
index 48d0b68b..7a90a5ab 100644
--- a/library/server/request/router/router.ecf
+++ b/library/server/request/router/router.ecf
@@ -7,14 +7,15 @@
/EIFGENs$
/.svn$
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/library/server/request/router/src/application/routed_application.e b/library/server/request/router/src/application/routed_application.e
new file mode 100644
index 00000000..a50fdb5d
--- /dev/null
+++ b/library/server/request/router/src/application/routed_application.e
@@ -0,0 +1,61 @@
+note
+ description: "Summary description for {ROUTED_APPLICATION}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+deferred class
+ ROUTED_APPLICATION
+
+feature -- Setup
+
+ initialize_router
+ -- Initialize `router'
+ do
+ create_router
+ setup_router
+ end
+
+ create_router
+ -- Create `router'
+ deferred
+ ensure
+ router_created: router /= Void
+ end
+
+ setup_router
+ -- Setup `router'
+ require
+ router_created: router /= Void
+ deferred
+ end
+
+ router: REQUEST_ROUTER
+ -- Request router
+
+feature -- Execution
+
+ execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM)
+ do
+ if attached router.dispatch (req, res) as r then
+ --| done
+ else
+ execute_default (req, res)
+ end
+ end
+
+ execute_default (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM)
+ deferred
+ end
+
+note
+ copyright: "2011-2011, Eiffel Software and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+ source: "[
+ Eiffel Software
+ 5949 Hollister Ave., Goleta, CA 93117 USA
+ Telephone 805-685-1006, Fax 805-685-6869
+ Website http://www.eiffel.com
+ Customer support http://support.eiffel.com
+ ]"
+end
diff --git a/library/server/request/router/src/context/request_handler_context.e b/library/server/request/router/src/context/request_handler_context.e
index 97f404d8..ca92756f 100644
--- a/library/server/request/router/src/context/request_handler_context.e
+++ b/library/server/request/router/src/context/request_handler_context.e
@@ -7,6 +7,14 @@ note
deferred class
REQUEST_HANDLER_CONTEXT
+inherit
+ ANY
+
+ REQUEST_FORMAT_UTILITY
+ export
+ {NONE} all
+ end
+
feature -- Access
request: EWSGI_REQUEST
@@ -15,23 +23,39 @@ feature -- Access
path: STRING
-- ???
- request_format: detachable STRING
- -- Request format based on `Content-Type'.
+ request_content_type (content_type_supported: detachable ARRAY [STRING]): detachable STRING
local
s: detachable STRING
+ i,n: INTEGER
do
- s := request.environment.content_type
- if s = Void then
- elseif s.same_string ({HTTP_CONSTANTS}.json_text) then
- Result := {HTTP_FORMAT_CONSTANTS}.json_name
- elseif s.same_string ({HTTP_CONSTANTS}.json_app) then
- Result := {HTTP_FORMAT_CONSTANTS}.json_name
- elseif s.same_string ({HTTP_CONSTANTS}.xml_text) then
- Result := {HTTP_FORMAT_CONSTANTS}.xml_name
- elseif s.same_string ({HTTP_CONSTANTS}.html_text) then
- Result := {HTTP_FORMAT_CONSTANTS}.html_name
- elseif s.same_string ({HTTP_CONSTANTS}.plain_text) then
- Result := {HTTP_FORMAT_CONSTANTS}.text_name
+ Result := request.environment.content_type
+ if Result = Void then
+ s := request.environment.http_accept
+ if s /= Void then
+ if attached accepted_content_types (request) as l_accept_lst then
+ from
+ l_accept_lst.start
+ until
+ l_accept_lst.after or Result /= Void
+ loop
+ s := l_accept_lst.item
+ if content_type_supported /= Void then
+ from
+ i := content_type_supported.lower
+ n := content_type_supported.upper
+ until
+ i > n or Result /= Void
+ loop
+ if content_type_supported[i].same_string (s) then
+ Result := s
+ end
+ i := i + 1
+ end
+ end
+ l_accept_lst.forth
+ end
+ end
+ end
end
end
diff --git a/library/server/request/router/src/utility/request_format_utility.e b/library/server/request/router/src/utility/request_format_utility.e
new file mode 100644
index 00000000..225d24f0
--- /dev/null
+++ b/library/server/request/router/src/utility/request_format_utility.e
@@ -0,0 +1,89 @@
+note
+ description: "Summary description for {REQUEST_FORMAT_UTILITY}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ REQUEST_FORMAT_UTILITY
+
+feature -- Access
+
+ accepted_content_types (req: EWSGI_REQUEST): detachable ARRAYED_LIST [STRING]
+ local
+ l_accept: detachable STRING
+ s,q: STRING
+ p: INTEGER
+ lst: LIST [STRING]
+ qs: QUICK_SORTER [STRING]
+ do
+ l_accept := req.environment.http_accept
+--TEST l_accept := "text/html,application/xhtml+xml;q=0.6,application/xml;q=0.2,text/plain;q=0.5,*/*;q=0.8"
+
+ if l_accept /= Void then
+ lst := l_accept.split (',')
+ create Result.make (lst.count)
+ from
+ lst.start
+ until
+ lst.after
+ loop
+ s := lst.item
+ p := s.substring_index (";q=", 1)
+ if p > 0 then
+ q := s.substring (p + 3, s.count)
+ s := s.substring (1, p - 1)
+ else
+ q := "1.0"
+ end
+ Result.force (q + ":" + s)
+
+ lst.forth
+ end
+ create qs.make (create {COMPARABLE_COMPARATOR [STRING]})
+ qs.reverse_sort (Result)
+ from
+ Result.start
+ until
+ Result.after
+ loop
+ s := Result.item
+ p := s.index_of (':', 1)
+ if p > 0 then
+ s.remove_head (p)
+ else
+ check should_have_colon: False end
+ end
+ Result.forth
+ end
+ end
+ end
+
+feature {NONE} -- Implementation
+
+ string_in_array (arr: ARRAY [STRING]; s: STRING): BOOLEAN
+ local
+ i,n: INTEGER
+ do
+ from
+ i := arr.lower
+ n := arr.upper
+ until
+ i > n or Result
+ loop
+ Result := s.same_string (arr[i])
+ i := i + 1
+ end
+ end
+
+note
+ copyright: "2011-2011, Eiffel Software and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+ source: "[
+ Eiffel Software
+ 5949 Hollister Ave., Goleta, CA 93117 USA
+ Telephone 805-685-1006, Fax 805-685-6869
+ Website http://www.eiffel.com
+ Customer support http://support.eiffel.com
+ ]"
+end