diff --git a/CHANGELOGS.txt b/CHANGELOGS.txt index 30faa5c0..4ea6deca 100644 --- a/CHANGELOGS.txt +++ b/CHANGELOGS.txt @@ -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. diff --git a/examples/hello_routed_world/src/hello_routed_world.e b/examples/hello_routed_world/src/hello_routed_world.e index d643869d..a29ce2e8 100644 --- a/examples/hello_routed_world/src/hello_routed_world.e +++ b/examples/hello_routed_world/src/hello_routed_world.e @@ -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 ("
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 diff --git a/library/protocol/http/src/http_format_constants.e b/library/protocol/http/src/http_format_constants.e index 3b4d91ba..b5d00397 100644 --- a/library/protocol/http/src/http_format_constants.e +++ b/library/protocol/http/src/http_format_constants.e @@ -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 diff --git a/library/protocol/uri_template/src/uri_template.e b/library/protocol/uri_template/src/uri_template.e index ddc9d0c6..73bd7255 100644 --- a/library/protocol/uri_template/src/uri_template.e +++ b/library/protocol/uri_template/src/uri_template.e @@ -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 diff --git a/library/protocol/uri_template/tests/test_uri_template.e b/library/protocol/uri_template/tests/test_uri_template.e index 77fa2700..4c1a2a1d 100644 --- a/library/protocol/uri_template/tests/test_uri_template.e +++ b/library/protocol/uri_template/tests/test_uri_template.e @@ -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 diff --git a/library/server/ewsgi/default/nino/default_wgi_application.e b/library/server/ewsgi/default/nino/default_wgi_application.e index f396a0fb..7ff198fa 100644 --- a/library/server/ewsgi/default/nino/default_wgi_application.e +++ b/library/server/ewsgi/default/nino/default_wgi_application.e @@ -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 diff --git a/library/server/request/rest/license.lic b/library/server/request/rest/license.lic new file mode 100644 index 00000000..c929225f --- /dev/null +++ b/library/server/request/rest/license.lic @@ -0,0 +1 @@ +reference:forum2 diff --git a/library/server/request/rest/rest-safe.ecf b/library/server/request/rest/rest-safe.ecf new file mode 100644 index 00000000..a6865a9a --- /dev/null +++ b/library/server/request/rest/rest-safe.ecf @@ -0,0 +1,28 @@ + +" + l_trace + "") +-- 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 (" start again%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 diff --git a/library/server/request/rest/tests/src/gateway/cgi/rest_application_gateway.e b/library/server/request/rest/tests/src/gateway/cgi/rest_application_gateway.e new file mode 100644 index 00000000..ab7b0997 --- /dev/null +++ b/library/server/request/rest/tests/src/gateway/cgi/rest_application_gateway.e @@ -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 diff --git a/library/server/request/rest/tests/src/gateway/fcgi/rest_application_gateway.e b/library/server/request/rest/tests/src/gateway/fcgi/rest_application_gateway.e new file mode 100644 index 00000000..f78f80e8 --- /dev/null +++ b/library/server/request/rest/tests/src/gateway/fcgi/rest_application_gateway.e @@ -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 diff --git a/library/server/request/rest/tests/src/gateway/nino/rest_application_gateway.e b/library/server/request/rest/tests/src/gateway/nino/rest_application_gateway.e new file mode 100644 index 00000000..af955abd --- /dev/null +++ b/library/server/request/rest/tests/src/gateway/nino/rest_application_gateway.e @@ -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 diff --git a/library/server/request/rest/tests/src/handler/app_application.e b/library/server/request/rest/tests/src/handler/app_application.e new file mode 100644 index 00000000..325f832f --- /dev/null +++ b/library/server/request/rest/tests/src/handler/app_application.e @@ -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 diff --git a/library/server/request/rest/tests/src/handler/app_request_agent_handler.e b/library/server/request/rest/tests/src/handler/app_request_agent_handler.e new file mode 100644 index 00000000..a59ab7ab --- /dev/null +++ b/library/server/request/rest/tests/src/handler/app_request_agent_handler.e @@ -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 diff --git a/library/server/request/rest/tests/src/handler/app_request_handler.e b/library/server/request/rest/tests/src/handler/app_request_handler.e new file mode 100644 index 00000000..f0298788 --- /dev/null +++ b/library/server/request/rest/tests/src/handler/app_request_handler.e @@ -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 ("
")
+ 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 ("")
+ 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
diff --git a/library/server/request/rest/tests/src/handler/app_request_handler_context.e b/library/server/request/rest/tests/src/handler/app_request_handler_context.e
new file mode 100644
index 00000000..98bd12de
--- /dev/null
+++ b/library/server/request/rest/tests/src/handler/app_request_handler_context.e
@@ -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
diff --git a/library/server/request/rest/tests/src/handler/app_request_helper.e b/library/server/request/rest/tests/src/handler/app_request_helper.e
new file mode 100644
index 00000000..77cde314
--- /dev/null
+++ b/library/server/request/rest/tests/src/handler/app_request_helper.e
@@ -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 := "" + a_error_message + "") + end + rep_data.set_message (s) + when {HTTP_FORMAT_CONSTANTS}.text then -- Default + s := "Application: " + a_path + "