From 512f2d2ce53fd53b7e6e688143eb4ff95a379e84 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 13 Sep 2011 17:08:40 +0200 Subject: [PATCH] Added first draft for RESTful library note: the interfaces are likely to change in the future --- library/server/request/rest/license.lic | 1 + library/server/request/rest/rest-safe.ecf | 28 ++ library/server/request/rest/rest.ecf | 28 ++ .../rest/src/application/rest_application.e | 31 ++ .../context/rest_request_handler_context.e | 78 +++++ .../rest_request_uri_handler_context.e | 28 ++ ...est_request_uri_template_handler_context.e | 28 ++ .../rest/src/contrib/doc/html/html_page.e | 184 ++++++++++ .../src/contrib/doc/html/html_page_head.e | 111 ++++++ .../src/contrib/doc/html/html_utilities.e | 90 +++++ .../src/contrib/doc/rest_api_documentation.e | 317 ++++++++++++++++++ .../doc/rest_api_documentation_html_page.e | 124 +++++++ .../rest_api_documentation_html_page_head.e | 75 +++++ .../src/default/default_rest_application.e | 23 ++ .../default_rest_request_uri_handler.e | 24 ++ .../default/default_rest_request_uri_router.e | 40 +++ ...efault_rest_request_uri_template_handler.e | 24 ++ ...default_rest_request_uri_template_router.e | 40 +++ ...est_request_uri_template_routing_handler.e | 33 ++ .../default/default_rest_uri_application.e | 30 ++ .../default_rest_uri_template_application.e | 30 ++ .../src/handler/rest_request_agent_handler.e | 56 ++++ .../src/handler/rest_request_group_handler.e- | 47 +++ .../rest/src/handler/rest_request_handler.e | 165 +++++++++ .../rest_request_uri_routing_handler.e | 51 +++ ...est_request_uri_template_routing_handler.e | 51 +++ .../request/rest/src/response/rest_response.e | 156 +++++++++ .../rest/src/router/rest_request_router.e | 23 ++ .../rest/src/router/rest_request_uri_router.e | 36 ++ .../router/rest_request_uri_template_router.e | 28 ++ .../request/rest/tests/htdocs/.htaccess | 26 ++ .../request/rest/tests/htdocs/README.txt | 31 ++ .../request/rest/tests/htdocs/build.eant | 24 ++ .../server/request/rest/tests/htdocs/htaccess | 26 ++ .../request/rest/tests/htdocs/sample.eapp | 0 library/server/request/rest/tests/sample.ecf | 55 +++ .../src/app/app_account_verify_credential.e | 109 ++++++ .../request/rest/tests/src/app/app_test.e | 95 ++++++ .../request/rest/tests/src/app_server.e | 148 ++++++++ .../gateway/cgi/rest_application_gateway.e | 24 ++ .../gateway/fcgi/rest_application_gateway.e | 35 ++ .../gateway/nino/rest_application_gateway.e | 44 +++ .../rest/tests/src/handler/app_application.e | 30 ++ .../src/handler/app_request_agent_handler.e | 32 ++ .../tests/src/handler/app_request_handler.e | 60 ++++ .../src/handler/app_request_handler_context.e | 86 +++++ .../tests/src/handler/app_request_helper.e | 101 ++++++ .../tests/src/handler/app_request_router.e | 26 ++ .../src/handler/app_request_routing_handler.e | 40 +++ 49 files changed, 2972 insertions(+) create mode 100644 library/server/request/rest/license.lic create mode 100644 library/server/request/rest/rest-safe.ecf create mode 100644 library/server/request/rest/rest.ecf create mode 100644 library/server/request/rest/src/application/rest_application.e create mode 100644 library/server/request/rest/src/context/rest_request_handler_context.e create mode 100644 library/server/request/rest/src/context/rest_request_uri_handler_context.e create mode 100644 library/server/request/rest/src/context/rest_request_uri_template_handler_context.e create mode 100644 library/server/request/rest/src/contrib/doc/html/html_page.e create mode 100644 library/server/request/rest/src/contrib/doc/html/html_page_head.e create mode 100644 library/server/request/rest/src/contrib/doc/html/html_utilities.e create mode 100644 library/server/request/rest/src/contrib/doc/rest_api_documentation.e create mode 100644 library/server/request/rest/src/contrib/doc/rest_api_documentation_html_page.e create mode 100644 library/server/request/rest/src/contrib/doc/rest_api_documentation_html_page_head.e create mode 100644 library/server/request/rest/src/default/default_rest_application.e create mode 100644 library/server/request/rest/src/default/default_rest_request_uri_handler.e create mode 100644 library/server/request/rest/src/default/default_rest_request_uri_router.e create mode 100644 library/server/request/rest/src/default/default_rest_request_uri_template_handler.e create mode 100644 library/server/request/rest/src/default/default_rest_request_uri_template_router.e create mode 100644 library/server/request/rest/src/default/default_rest_request_uri_template_routing_handler.e create mode 100644 library/server/request/rest/src/default/default_rest_uri_application.e create mode 100644 library/server/request/rest/src/default/default_rest_uri_template_application.e create mode 100644 library/server/request/rest/src/handler/rest_request_agent_handler.e create mode 100644 library/server/request/rest/src/handler/rest_request_group_handler.e- create mode 100644 library/server/request/rest/src/handler/rest_request_handler.e create mode 100644 library/server/request/rest/src/handler/rest_request_uri_routing_handler.e create mode 100644 library/server/request/rest/src/handler/rest_request_uri_template_routing_handler.e create mode 100644 library/server/request/rest/src/response/rest_response.e create mode 100644 library/server/request/rest/src/router/rest_request_router.e create mode 100644 library/server/request/rest/src/router/rest_request_uri_router.e create mode 100644 library/server/request/rest/src/router/rest_request_uri_template_router.e create mode 100644 library/server/request/rest/tests/htdocs/.htaccess create mode 100644 library/server/request/rest/tests/htdocs/README.txt create mode 100644 library/server/request/rest/tests/htdocs/build.eant create mode 100644 library/server/request/rest/tests/htdocs/htaccess create mode 100644 library/server/request/rest/tests/htdocs/sample.eapp create mode 100644 library/server/request/rest/tests/sample.ecf create mode 100644 library/server/request/rest/tests/src/app/app_account_verify_credential.e create mode 100644 library/server/request/rest/tests/src/app/app_test.e create mode 100644 library/server/request/rest/tests/src/app_server.e create mode 100644 library/server/request/rest/tests/src/gateway/cgi/rest_application_gateway.e create mode 100644 library/server/request/rest/tests/src/gateway/fcgi/rest_application_gateway.e create mode 100644 library/server/request/rest/tests/src/gateway/nino/rest_application_gateway.e create mode 100644 library/server/request/rest/tests/src/handler/app_application.e create mode 100644 library/server/request/rest/tests/src/handler/app_request_agent_handler.e create mode 100644 library/server/request/rest/tests/src/handler/app_request_handler.e create mode 100644 library/server/request/rest/tests/src/handler/app_request_handler_context.e create mode 100644 library/server/request/rest/tests/src/handler/app_request_helper.e create mode 100644 library/server/request/rest/tests/src/handler/app_request_router.e create mode 100644 library/server/request/rest/tests/src/handler/app_request_routing_handler.e 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 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + diff --git a/library/server/request/rest/rest.ecf b/library/server/request/rest/rest.ecf new file mode 100644 index 00000000..0c5c4034 --- /dev/null +++ b/library/server/request/rest/rest.ecf @@ -0,0 +1,28 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + diff --git a/library/server/request/rest/src/application/rest_application.e b/library/server/request/rest/src/application/rest_application.e new file mode 100644 index 00000000..b4536020 --- /dev/null +++ b/library/server/request/rest/src/application/rest_application.e @@ -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 diff --git a/library/server/request/rest/src/context/rest_request_handler_context.e b/library/server/request/rest/src/context/rest_request_handler_context.e new file mode 100644 index 00000000..4fbf9913 --- /dev/null +++ b/library/server/request/rest/src/context/rest_request_handler_context.e @@ -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 diff --git a/library/server/request/rest/src/context/rest_request_uri_handler_context.e b/library/server/request/rest/src/context/rest_request_uri_handler_context.e new file mode 100644 index 00000000..d9f91e76 --- /dev/null +++ b/library/server/request/rest/src/context/rest_request_uri_handler_context.e @@ -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 diff --git a/library/server/request/rest/src/context/rest_request_uri_template_handler_context.e b/library/server/request/rest/src/context/rest_request_uri_template_handler_context.e new file mode 100644 index 00000000..eefa61e7 --- /dev/null +++ b/library/server/request/rest/src/context/rest_request_uri_template_handler_context.e @@ -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 diff --git a/library/server/request/rest/src/contrib/doc/html/html_page.e b/library/server/request/rest/src/contrib/doc/html/html_page.e new file mode 100644 index 00000000..d203a8ab --- /dev/null +++ b/library/server/request/rest/src/contrib/doc/html/html_page.e @@ -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 ("%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 ("%N") + s.append_string (body) + s.append_string ("") + + --| End + s.append_string ("") + + --| 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 diff --git a/library/server/request/rest/src/contrib/doc/html/html_page_head.e b/library/server/request/rest/src/contrib/doc/html/html_page_head.e new file mode 100644 index 00000000..c51d930e --- /dev/null +++ b/library/server/request/rest/src/contrib/doc/html/html_page_head.e @@ -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 ("" + t + "%N") + end + if text.count > 0 then + s.append_string (text) + s.append_character ('%N') + end + if s.count > 0 then + internal_string := "%N" + s + "" + 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 diff --git a/library/server/request/rest/src/contrib/doc/html/html_utilities.e b/library/server/request/rest/src/contrib/doc/html/html_utilities.e new file mode 100644 index 00000000..18261931 --- /dev/null +++ b/library/server/request/rest/src/contrib/doc/html/html_utilities.e @@ -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 ("&") + when '%"' then + Result.append_string (""") + when '%'' then + Result.append_string ("'") + when '<' then + Result.append_string ("<") + when '>' then + Result.append_string (">") + 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 diff --git a/library/server/request/rest/src/contrib/doc/rest_api_documentation.e b/library/server/request/rest/src/contrib/doc/rest_api_documentation.e new file mode 100644 index 00000000..0d30b40e --- /dev/null +++ b/library/server/request/rest/src/contrib/doc/rest_api_documentation.e @@ -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 ("
") + s.append_string ("

") + + s.append_string (".. Show all features ..") + s.append_string ("

%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 ("") + 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 ("
") + s.append_string ("

"+ a_resource +"

") + s.append_string ("
") +-- if rq.hidden (req) then +-- s.append_string ("
This feature is hidden
%N") +-- else + if attached rq.description as desc then + s.append_string ("
" + desc + "
") + end +-- if attached {REST_REQUEST_GROUP_HANDLER} rq as grp then +-- s.append_string ("
Handler: ") +-- if attached grp.handlers.new_cursor as l_handlers_cursor then +-- from + +-- until +-- l_handlers_cursor.after +-- loop +-- s.append_string (" ") +-- s.append_string (""+ l_handlers_cursor.item.path +"") +-- l_handlers_cursor.forth +-- end +-- end +-- s.append_string ("
") +-- end + if attached rq.supported_format_names as l_formats and then not l_formats.is_empty then + s.append_string ("
Supported formats: ") + if attached l_formats.new_cursor as l_formats_cursor then + from + + until + l_formats_cursor.after + loop + s.append_string (" ") + s.append_string (""+ l_formats_cursor.item +"") + l_formats_cursor.forth + end + end + s.append_string ("
") + end + if attached rq.supported_request_method_names as l_methods and then not l_methods.is_empty then + s.append_string ("
Supported request methods: ") + 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 ("
") + end + s.append_string ("
Authentication required: " + rq.authentication_required.out + "
") + 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 ("
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 ("" + l_query_param + "") + s.append_string ("=" + l_query_param + "") + end + l_query_variable_names.forth + end + s.append_string ("
%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 ("
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 ("{" + l_seg_param + "}") + end + l_path_variable_names.forth + end + s.append_string ("
%N") + end + + end +-- if attached rq._parameters as l_uri_params and then not l_uri_params.is_empty then +-- s.append_string ("
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 ("/" + l_uri_param.name + "") +-- s.append_string ("/{" + l_uri_param.name + "}") +-- end +-- l_uri_params_cursor.forth +-- end +-- end +-- s.append_string ("
%N") +-- end +-- if attached rq.parameters as l_params and then not l_params.is_empty then +-- s.append_string ("
Parameters: ") + +-- --| show form only if we have a default format +-- if l_dft_format_name = Void then +-- s.append_string ("to test the parameter(s), please first select a supported format.%N") +-- else +-- if rq.method_post_supported then +-- s.append_string ("
%N") +-- else +-- s.append_string ("%N") +-- end +-- end +-- s.append_string ("
    ") +-- 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 ("
  • " + l_param.name + "") +-- if l_param.optional then +-- s.append_string (" (Optional)") +-- end +-- if attached l_param.description as l_param_desc then +-- s.append_string (": " + l_param_desc + "") +-- end +-- if l_dft_format_name /= Void then +-- s.append (" ") +-- end +-- s.append_string ("
  • ") +-- end +-- l_params_cursor.forth +-- end +-- end + +-- if l_dft_format_name /= Void then +-- s.append_string ("") +-- s.append_string ("") +-- end +-- s.append_string ("
") +-- else +-- if l_dft_format_name /= Void then +-- s.append_string ("Test "+ a_resource + "." + l_dft_format_name + "") +-- else +-- s.append_string ("Test "+ a_resource +"") +-- end +-- end + s.append_string ("
%N") +-- end + s.append_string ("
%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 diff --git a/library/server/request/rest/src/contrib/doc/rest_api_documentation_html_page.e b/library/server/request/rest/src/contrib/doc/rest_api_documentation_html_page.e new file mode 100644 index 00000000..953cb482 --- /dev/null +++ b/library/server/request/rest/src/contrib/doc/rest_api_documentation_html_page.e @@ -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 ("Shortcuts: | ") + shortcuts.start + until + shortcuts.after + loop + Result.append_string (""+ shortcuts.item.name +" | ") + 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 := "

" + bt + "

%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 diff --git a/library/server/request/rest/src/contrib/doc/rest_api_documentation_html_page_head.e b/library/server/request/rest/src/contrib/doc/rest_api_documentation_html_page_head.e new file mode 100644 index 00000000..aa57b34f --- /dev/null +++ b/library/server/request/rest/src/contrib/doc/rest_api_documentation_html_page_head.e @@ -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 ("", 1) + if p > 0 then + s.insert_string ("%N", p) + else + s.append_string ("%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 diff --git a/library/server/request/rest/src/default/default_rest_application.e b/library/server/request/rest/src/default/default_rest_application.e new file mode 100644 index 00000000..3c8ee966 --- /dev/null +++ b/library/server/request/rest/src/default/default_rest_application.e @@ -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 diff --git a/library/server/request/rest/src/default/default_rest_request_uri_handler.e b/library/server/request/rest/src/default/default_rest_request_uri_handler.e new file mode 100644 index 00000000..efbf0ed7 --- /dev/null +++ b/library/server/request/rest/src/default/default_rest_request_uri_handler.e @@ -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 diff --git a/library/server/request/rest/src/default/default_rest_request_uri_router.e b/library/server/request/rest/src/default/default_rest_request_uri_router.e new file mode 100644 index 00000000..854f9c68 --- /dev/null +++ b/library/server/request/rest/src/default/default_rest_request_uri_router.e @@ -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 diff --git a/library/server/request/rest/src/default/default_rest_request_uri_template_handler.e b/library/server/request/rest/src/default/default_rest_request_uri_template_handler.e new file mode 100644 index 00000000..a637ed60 --- /dev/null +++ b/library/server/request/rest/src/default/default_rest_request_uri_template_handler.e @@ -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 diff --git a/library/server/request/rest/src/default/default_rest_request_uri_template_router.e b/library/server/request/rest/src/default/default_rest_request_uri_template_router.e new file mode 100644 index 00000000..2054255b --- /dev/null +++ b/library/server/request/rest/src/default/default_rest_request_uri_template_router.e @@ -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 diff --git a/library/server/request/rest/src/default/default_rest_request_uri_template_routing_handler.e b/library/server/request/rest/src/default/default_rest_request_uri_template_routing_handler.e new file mode 100644 index 00000000..67c37924 --- /dev/null +++ b/library/server/request/rest/src/default/default_rest_request_uri_template_routing_handler.e @@ -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 diff --git a/library/server/request/rest/src/default/default_rest_uri_application.e b/library/server/request/rest/src/default/default_rest_uri_application.e new file mode 100644 index 00000000..b6bdd088 --- /dev/null +++ b/library/server/request/rest/src/default/default_rest_uri_application.e @@ -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 diff --git a/library/server/request/rest/src/default/default_rest_uri_template_application.e b/library/server/request/rest/src/default/default_rest_uri_template_application.e new file mode 100644 index 00000000..0f1e76c6 --- /dev/null +++ b/library/server/request/rest/src/default/default_rest_uri_template_application.e @@ -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 diff --git a/library/server/request/rest/src/handler/rest_request_agent_handler.e b/library/server/request/rest/src/handler/rest_request_agent_handler.e new file mode 100644 index 00000000..ba85571e --- /dev/null +++ b/library/server/request/rest/src/handler/rest_request_agent_handler.e @@ -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 diff --git a/library/server/request/rest/src/handler/rest_request_group_handler.e- b/library/server/request/rest/src/handler/rest_request_group_handler.e- new file mode 100644 index 00000000..12bfe4b0 --- /dev/null +++ b/library/server/request/rest/src/handler/rest_request_group_handler.e- @@ -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 diff --git a/library/server/request/rest/src/handler/rest_request_handler.e b/library/server/request/rest/src/handler/rest_request_handler.e new file mode 100644 index 00000000..8e9810a8 --- /dev/null +++ b/library/server/request/rest/src/handler/rest_request_handler.e @@ -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 diff --git a/library/server/request/rest/src/handler/rest_request_uri_routing_handler.e b/library/server/request/rest/src/handler/rest_request_uri_routing_handler.e new file mode 100644 index 00000000..6d6fb2f1 --- /dev/null +++ b/library/server/request/rest/src/handler/rest_request_uri_routing_handler.e @@ -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 diff --git a/library/server/request/rest/src/handler/rest_request_uri_template_routing_handler.e b/library/server/request/rest/src/handler/rest_request_uri_template_routing_handler.e new file mode 100644 index 00000000..606df5dc --- /dev/null +++ b/library/server/request/rest/src/handler/rest_request_uri_template_routing_handler.e @@ -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 diff --git a/library/server/request/rest/src/response/rest_response.e b/library/server/request/rest/src/response/rest_response.e new file mode 100644 index 00000000..f385b9fe --- /dev/null +++ b/library/server/request/rest/src/response/rest_response.e @@ -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 diff --git a/library/server/request/rest/src/router/rest_request_router.e b/library/server/request/rest/src/router/rest_request_router.e new file mode 100644 index 00000000..f6755362 --- /dev/null +++ b/library/server/request/rest/src/router/rest_request_router.e @@ -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 diff --git a/library/server/request/rest/src/router/rest_request_uri_router.e b/library/server/request/rest/src/router/rest_request_uri_router.e new file mode 100644 index 00000000..57a0a362 --- /dev/null +++ b/library/server/request/rest/src/router/rest_request_uri_router.e @@ -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 diff --git a/library/server/request/rest/src/router/rest_request_uri_template_router.e b/library/server/request/rest/src/router/rest_request_uri_template_router.e new file mode 100644 index 00000000..d023dd3e --- /dev/null +++ b/library/server/request/rest/src/router/rest_request_uri_template_router.e @@ -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 diff --git a/library/server/request/rest/tests/htdocs/.htaccess b/library/server/request/rest/tests/htdocs/.htaccess new file mode 100644 index 00000000..687d4c26 --- /dev/null +++ b/library/server/request/rest/tests/htdocs/.htaccess @@ -0,0 +1,26 @@ +Options Indexes FollowSymLinks ExecCGI + + +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 + + + +Options +ExecCGI +AddHandler cgi-script exe + + + + 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] + + diff --git a/library/server/request/rest/tests/htdocs/README.txt b/library/server/request/rest/tests/htdocs/README.txt new file mode 100644 index 00000000..13bd0686 --- /dev/null +++ b/library/server/request/rest/tests/htdocs/README.txt @@ -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 + + FcgidIdleTimeout 60 + FcgidBusyScanInterval 120 + FcgidProcessLifeTime 1600 + #7200 + FcgidMaxProcesses 5 + FcgidMaxProcessesPerClass 100 + FcgidMinProcessesPerClass 100 + FcgidConnectTimeout 8 + FcgidIOTimeout 3000 + FcgidBusyTimeout 3200 + FcgidMaxRequestLen 10000000 + FcgidPassHeader Authorization + + + + alias /REST/ "c:/_dev/Dev-Process/src/server/htdocs/" + + AllowOverride All + Order allow,deny + Allow from all + + +* You can use , but then the AllowOverride All, should be somewhere else, since does not allow it. + +* And then, check the .htaccess from this folder for additional settings (but required) + + diff --git a/library/server/request/rest/tests/htdocs/build.eant b/library/server/request/rest/tests/htdocs/build.eant new file mode 100644 index 00000000..2a5a919b --- /dev/null +++ b/library/server/request/rest/tests/htdocs/build.eant @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/server/request/rest/tests/htdocs/htaccess b/library/server/request/rest/tests/htdocs/htaccess new file mode 100644 index 00000000..a36c2b28 --- /dev/null +++ b/library/server/request/rest/tests/htdocs/htaccess @@ -0,0 +1,26 @@ +Options Indexes FollowSymLinks ExecCGI + + +AddHandler fcgid-script .eapp +FcgidWrapper ##SAMPLE-CGI-DIR##/sample.exe .eapp + + + +Options +ExecCGI +AddHandler cgi-script exe + + + + 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] + + diff --git a/library/server/request/rest/tests/htdocs/sample.eapp b/library/server/request/rest/tests/htdocs/sample.eapp new file mode 100644 index 00000000..e69de29b diff --git a/library/server/request/rest/tests/sample.ecf b/library/server/request/rest/tests/sample.ecf new file mode 100644 index 00000000..05cfaeab --- /dev/null +++ b/library/server/request/rest/tests/sample.ecf @@ -0,0 +1,55 @@ + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + + + + + + + + + + + + + + /gateway$ + + + + + + + + + + + /gateway$ + + + + + + + + + + + + + + + /gateway$ + + + + + diff --git a/library/server/request/rest/tests/src/app/app_account_verify_credential.e b/library/server/request/rest/tests/src/app/app_account_verify_credential.e new file mode 100644 index 00000000..0bb06270 --- /dev/null +++ b/library/server/request/rest/tests/src/app/app_account_verify_credential.e @@ -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 ("" + l_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 diff --git a/library/server/request/rest/tests/src/app/app_test.e b/library/server/request/rest/tests/src/app/app_test.e new file mode 100644 index 00000000..5d3f1ca2 --- /dev/null +++ b/library/server/request/rest/tests/src/app/app_test.e @@ -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 ("
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 + "
%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 + "
%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 diff --git a/library/server/request/rest/tests/src/app_server.e b/library/server/request/rest/tests/src/app_server.e new file mode 100644 index 00000000..51c6399e --- /dev/null +++ b/library/server/request/rest/tests/src/app_server.e @@ -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.
%N"; + s.append ("You are being redirected to /doc 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 ("
" + 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 := "" + if a_error_message /= Void then + s.append_string (a_error_message) + end + s.append_string ("") + rep_data.set_message (s) + when {HTTP_FORMAT_CONSTANTS}.html then + rep_data.headers.put_content_type_text_html + s := "application: " + a_path + "
%NError (" + a_error_id.out + ") %"" + a_error_name + "%"
%N" + if a_error_message /= Void then + s.append_string ("
" + a_error_message + "
") + end + rep_data.set_message (s) + when {HTTP_FORMAT_CONSTANTS}.text then -- Default + s := "Application: " + a_path + "
%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 diff --git a/library/server/request/rest/tests/src/handler/app_request_router.e b/library/server/request/rest/tests/src/handler/app_request_router.e new file mode 100644 index 00000000..25bd2a14 --- /dev/null +++ b/library/server/request/rest/tests/src/handler/app_request_router.e @@ -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 diff --git a/library/server/request/rest/tests/src/handler/app_request_routing_handler.e b/library/server/request/rest/tests/src/handler/app_request_routing_handler.e new file mode 100644 index 00000000..e7bcee5e --- /dev/null +++ b/library/server/request/rest/tests/src/handler/app_request_routing_handler.e @@ -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