From 801caa4e69c2dd3ea42f8c5f4aca7d6c40777682 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 29 Jul 2011 15:13:08 +0200 Subject: [PATCH] added hello_routed_world example few changes on new `router' library (still in-progress) --- examples/hello_routed_world/hello-safe.ecf | 24 +++ examples/hello_routed_world/license.lic | 10 + .../src/framework/routed_application_helper.e | 145 ++++++++++++++ .../src/hello_routed_world.e | 183 ++++++++++++++++++ library/server/request/router/router-safe.ecf | 1 + library/server/request/router/router.ecf | 13 +- .../src/application/routed_application.e | 61 ++++++ .../src/context/request_handler_context.e | 52 +++-- .../src/utility/request_format_utility.e | 89 +++++++++ 9 files changed, 558 insertions(+), 20 deletions(-) create mode 100644 examples/hello_routed_world/hello-safe.ecf create mode 100644 examples/hello_routed_world/license.lic create mode 100644 examples/hello_routed_world/src/framework/routed_application_helper.e create mode 100644 examples/hello_routed_world/src/hello_routed_world.e create mode 100644 library/server/request/router/src/application/routed_application.e create mode 100644 library/server/request/router/src/utility/request_format_utility.e 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