diff --git a/examples/hello_routed_world/hello-safe.ecf b/examples/hello_routed_world/hello-safe.ecf index 294b85fe..ceaf6e38 100644 --- a/examples/hello_routed_world/hello-safe.ecf +++ b/examples/hello_routed_world/hello-safe.ecf @@ -13,7 +13,6 @@ - diff --git a/examples/hello_routed_world/src/framework/routed_application_helper.e b/examples/hello_routed_world/src/framework/routed_application_helper.e index 23129e61..a0430b11 100644 --- a/examples/hello_routed_world/src/framework/routed_application_helper.e +++ b/examples/hello_routed_world/src/framework/routed_application_helper.e @@ -17,7 +17,7 @@ inherit feature -- Helper - execute_content_type_not_allowed (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM; a_content_types: detachable ARRAY [STRING]; a_uri_formats: detachable ARRAY [STRING]) + execute_content_type_not_allowed (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER; a_content_types: detachable ARRAY [STRING]; a_uri_formats: detachable ARRAY [STRING]) local s, uri_s: detachable STRING i, n: INTEGER @@ -68,7 +68,7 @@ feature -- Helper end end - execute_method_not_allowed (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM; a_methods: ARRAY [STRING]) + execute_method_not_allowed (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER; a_methods: ARRAY [STRING]) local s: STRING i, n: INTEGER @@ -94,13 +94,13 @@ feature -- Helper >>) res.write_string ("Unsupported request method, Allow: " + s + "%N") end - + feature -- Context helper request_format_id (ctx: REQUEST_HANDLER_CONTEXT; a_format_variable_name: detachable STRING; content_type_supported: detachable ARRAY [STRING]): INTEGER -- Format id for the request based on {HTTP_FORMAT_CONSTANTS} local - l_format, l_content_type: detachable STRING_8 + l_format: detachable STRING_8 do if a_format_variable_name /= Void and then attached ctx.parameter (a_format_variable_name) as ctx_format then l_format := ctx_format.as_string_8 diff --git a/examples/hello_routed_world/src/hello_routed_world.e b/examples/hello_routed_world/src/hello_routed_world.e index 4dd44baa..2e0f5d1b 100644 --- a/examples/hello_routed_world/src/hello_routed_world.e +++ b/examples/hello_routed_world/src/hello_routed_world.e @@ -51,7 +51,7 @@ feature {NONE} -- Initialization feature -- Execution - execute_default (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + execute_default (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) local h: GW_HEADER l_url: STRING @@ -96,7 +96,7 @@ feature -- Execution res.flush end - execute_home (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + execute_home (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) do res.write_header (200, <<["Content-Type", "text/html"]>>) res.write_string ("Hello World ?!%N") @@ -116,7 +116,7 @@ feature -- Execution res.write_string ("%N") end - execute_hello (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM; a_name: detachable STRING_32; ctx: REQUEST_HANDLER_CONTEXT) + execute_hello (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER; a_name: detachable STRING_32; ctx: REQUEST_HANDLER_CONTEXT) local l_response_content_type: detachable STRING msg: STRING @@ -160,12 +160,12 @@ feature -- Execution end end - handle_hello (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + handle_hello (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) do execute_hello (req, res, Void, ctx) end - handle_anonymous_hello (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + handle_anonymous_hello (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) do execute_hello (req, res, ctx.parameter ("name"), ctx) end diff --git a/library/server/ewsgi/connectors/cgi/cgi-safe.ecf b/library/server/ewsgi/connectors/cgi/cgi-safe.ecf index af6c0537..512dd955 100644 --- a/library/server/ewsgi/connectors/cgi/cgi-safe.ecf +++ b/library/server/ewsgi/connectors/cgi/cgi-safe.ecf @@ -10,7 +10,7 @@ - + diff --git a/library/server/ewsgi/connectors/cgi/cgi.ecf b/library/server/ewsgi/connectors/cgi/cgi.ecf index c5039dd5..d6c265ec 100644 --- a/library/server/ewsgi/connectors/cgi/cgi.ecf +++ b/library/server/ewsgi/connectors/cgi/cgi.ecf @@ -10,7 +10,7 @@ - + diff --git a/library/server/ewsgi/connectors/libfcgi/libfcgi-safe.ecf b/library/server/ewsgi/connectors/libfcgi/libfcgi-safe.ecf index e4e72c59..875f24c0 100644 --- a/library/server/ewsgi/connectors/libfcgi/libfcgi-safe.ecf +++ b/library/server/ewsgi/connectors/libfcgi/libfcgi-safe.ecf @@ -10,7 +10,7 @@ - + diff --git a/library/server/ewsgi/connectors/libfcgi/libfcgi.ecf b/library/server/ewsgi/connectors/libfcgi/libfcgi.ecf index 247e480f..bf084003 100644 --- a/library/server/ewsgi/connectors/libfcgi/libfcgi.ecf +++ b/library/server/ewsgi/connectors/libfcgi/libfcgi.ecf @@ -10,7 +10,7 @@ - + diff --git a/library/server/ewsgi/connectors/nino/nino-safe.ecf b/library/server/ewsgi/connectors/nino/nino-safe.ecf index 635aea99..746f7baf 100644 --- a/library/server/ewsgi/connectors/nino/nino-safe.ecf +++ b/library/server/ewsgi/connectors/nino/nino-safe.ecf @@ -10,7 +10,7 @@ - + diff --git a/library/server/ewsgi/connectors/nino/nino.ecf b/library/server/ewsgi/connectors/nino/nino.ecf index c12d3562..68818796 100644 --- a/library/server/ewsgi/connectors/nino/nino.ecf +++ b/library/server/ewsgi/connectors/nino/nino.ecf @@ -10,7 +10,7 @@ - + diff --git a/library/server/ewsgi/default/cgi/default_ewsgi_application.e b/library/server/ewsgi/default/cgi/default_ewsgi_application.e index 1e582479..a4860978 100644 --- a/library/server/ewsgi/default/cgi/default_ewsgi_application.e +++ b/library/server/ewsgi/default/cgi/default_ewsgi_application.e @@ -7,7 +7,7 @@ deferred class DEFAULT_EWSGI_APPLICATION inherit - GW_APPLICATION_IMP + EWSGI_APPLICATION feature {NONE} -- Initialization diff --git a/library/server/ewsgi/default/ewsgi_cgi-safe.ecf b/library/server/ewsgi/default/ewsgi_cgi-safe.ecf index 3a97046d..21f4677f 100644 --- a/library/server/ewsgi/default/ewsgi_cgi-safe.ecf +++ b/library/server/ewsgi/default/ewsgi_cgi-safe.ecf @@ -11,7 +11,6 @@ - diff --git a/library/server/ewsgi/default/ewsgi_cgi.ecf b/library/server/ewsgi/default/ewsgi_cgi.ecf index b5f163cb..5abbca2c 100644 --- a/library/server/ewsgi/default/ewsgi_cgi.ecf +++ b/library/server/ewsgi/default/ewsgi_cgi.ecf @@ -11,7 +11,6 @@ - diff --git a/library/server/ewsgi/default/ewsgi_nino-safe.ecf b/library/server/ewsgi/default/ewsgi_nino-safe.ecf index ad20e21c..8a827054 100644 --- a/library/server/ewsgi/default/ewsgi_nino-safe.ecf +++ b/library/server/ewsgi/default/ewsgi_nino-safe.ecf @@ -11,7 +11,6 @@ - diff --git a/library/server/ewsgi/default/ewsgi_nino.ecf b/library/server/ewsgi/default/ewsgi_nino.ecf index 465410c5..0e3f9479 100644 --- a/library/server/ewsgi/default/ewsgi_nino.ecf +++ b/library/server/ewsgi/default/ewsgi_nino.ecf @@ -11,7 +11,6 @@ - diff --git a/library/server/ewsgi/default/nino/default_ewsgi_application.e b/library/server/ewsgi/default/nino/default_ewsgi_application.e index 7f8debfc..b7dabb28 100644 --- a/library/server/ewsgi/default/nino/default_ewsgi_application.e +++ b/library/server/ewsgi/default/nino/default_ewsgi_application.e @@ -7,7 +7,7 @@ deferred class DEFAULT_EWSGI_APPLICATION inherit - GW_APPLICATION_IMP + EWSGI_APPLICATION feature {NONE} -- Initialization diff --git a/library/server/ewsgi/default/nino/nino_application.e b/library/server/ewsgi/default/nino/nino_application.e index dd33bdd5..90ab66b7 100644 --- a/library/server/ewsgi/default/nino/nino_application.e +++ b/library/server/ewsgi/default/nino/nino_application.e @@ -12,16 +12,16 @@ create feature {NONE} -- Implementation - make (a_callback: like {GW_AGENT_APPLICATION}.callback) + make (a_callback: like {EWSGI_AGENT_APPLICATION}.callback) -- Initialize `Current'. do make_custom (a_callback, Void) end - make_custom (a_callback: like {GW_AGENT_APPLICATION}.callback; a_base_url: detachable STRING) + make_custom (a_callback: like {EWSGI_AGENT_APPLICATION}.callback; a_base_url: detachable STRING) -- Initialize `Current'. local - app: GW_AGENT_APPLICATION + app: EWSGI_AGENT_APPLICATION do create app.make (a_callback) create connector.make_with_base (app, a_base_url) diff --git a/library/server/ewsgi/ewsgi-full-safe.ecf b/library/server/ewsgi/ewsgi-full-safe.ecf index 42397e7d..8e81a9df 100644 --- a/library/server/ewsgi/ewsgi-full-safe.ecf +++ b/library/server/ewsgi/ewsgi-full-safe.ecf @@ -1,5 +1,5 @@ - + @@ -12,7 +12,6 @@ - diff --git a/library/server/ewsgi/ewsgi-full.ecf b/library/server/ewsgi/ewsgi-full.ecf index 0c5ea0b7..c40b7779 100644 --- a/library/server/ewsgi/ewsgi-full.ecf +++ b/library/server/ewsgi/ewsgi-full.ecf @@ -1,5 +1,5 @@ - + @@ -10,7 +10,7 @@ - + diff --git a/library/server/ewsgi/ewsgi-safe.ecf b/library/server/ewsgi/ewsgi-safe.ecf index ce68e823..c8014c95 100644 --- a/library/server/ewsgi/ewsgi-safe.ecf +++ b/library/server/ewsgi/ewsgi-safe.ecf @@ -11,7 +11,6 @@ - diff --git a/library/server/ewsgi/ewsgi.ecf b/library/server/ewsgi/ewsgi.ecf index bde71ac5..9cdcba23 100644 --- a/library/server/ewsgi/ewsgi.ecf +++ b/library/server/ewsgi/ewsgi.ecf @@ -11,7 +11,6 @@ - diff --git a/library/server/ewsgi/ewsgi_specification-safe.ecf b/library/server/ewsgi/ewsgi_spec-safe.ecf similarity index 89% rename from library/server/ewsgi/ewsgi_specification-safe.ecf rename to library/server/ewsgi/ewsgi_spec-safe.ecf index b66cd0e8..695a3356 100644 --- a/library/server/ewsgi/ewsgi_specification-safe.ecf +++ b/library/server/ewsgi/ewsgi_spec-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/ewsgi/ewsgi_specification.ecf b/library/server/ewsgi/ewsgi_spec.ecf similarity index 89% rename from library/server/ewsgi/ewsgi_specification.ecf rename to library/server/ewsgi/ewsgi_spec.ecf index b0721d07..59bfa9e0 100644 --- a/library/server/ewsgi/ewsgi_specification.ecf +++ b/library/server/ewsgi/ewsgi_spec.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/ewsgi/examples/hello_world/hello-safe.ecf b/library/server/ewsgi/examples/hello_world/hello-safe.ecf index 227a433e..aabd666f 100644 --- a/library/server/ewsgi/examples/hello_world/hello-safe.ecf +++ b/library/server/ewsgi/examples/hello_world/hello-safe.ecf @@ -13,7 +13,6 @@ - diff --git a/library/server/ewsgi/examples/hello_world/hello.ecf b/library/server/ewsgi/examples/hello_world/hello.ecf index f9d6b5cd..2eea409b 100644 --- a/library/server/ewsgi/examples/hello_world/hello.ecf +++ b/library/server/ewsgi/examples/hello_world/hello.ecf @@ -13,7 +13,6 @@ - diff --git a/library/server/ewsgi/examples/hello_world/src/hello_world.e b/library/server/ewsgi/examples/hello_world/src/hello_world.e index 367731c4..957c10a0 100644 --- a/library/server/ewsgi/examples/hello_world/src/hello_world.e +++ b/library/server/ewsgi/examples/hello_world/src/hello_world.e @@ -18,7 +18,7 @@ feature {NONE} -- Initialization (create {NINO_APPLICATION}.make_custom (agent execute, "")).listen (port_number) end - execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) do res.write_header (200, <<["Content-Type", "text/plain"]>>) res.write_string ("Hello World!%N") diff --git a/library/server/ewsgi/examples/hello_world_with_response/hello-safe.ecf b/library/server/ewsgi/examples/hello_world_with_response/hello-safe.ecf index ca270a86..ee350071 100644 --- a/library/server/ewsgi/examples/hello_world_with_response/hello-safe.ecf +++ b/library/server/ewsgi/examples/hello_world_with_response/hello-safe.ecf @@ -10,7 +10,6 @@ - diff --git a/library/server/ewsgi/specification/ewsgi_agent_application.e b/library/server/ewsgi/specification/ewsgi_agent_application.e deleted file mode 100644 index f2e2c521..00000000 --- a/library/server/ewsgi/specification/ewsgi_agent_application.e +++ /dev/null @@ -1,66 +0,0 @@ -note - description: "Summary description for {EWSGI_AGENT_APPLICATION}." - author: "" - date: "$Date$" - revision: "$Revision$" - -class - EWSGI_AGENT_APPLICATION - -inherit - EWSGI_APPLICATION - -create - make - -feature {NONE} -- Implementation - - make (a_callback: like callback; a_request_creator: like request_creator; a_response_creator: like response_creator) - -- Initialize `Current'. - do - callback := a_callback - request_creator := a_request_creator - response_creator := a_response_creator - end - -feature {NONE} -- Implementation - - request_creator: FUNCTION [ANY, TUPLE [env: EWSGI_ENVIRONMENT; input: EWSGI_INPUT_STREAM], EWSGI_REQUEST] - - response_creator: FUNCTION [ANY, TUPLE [req: EWSGI_REQUEST; output: EWSGI_OUTPUT_STREAM], EWSGI_RESPONSE_STREAM] - - callback: PROCEDURE [ANY, TUPLE [req: like new_request; res: like new_response]] - -- Procedure called on `execute' - - execute (req: like new_request; res: like new_response) - -- Execute the request - do - callback.call ([req, res]) - end - -feature -- Factory - - new_request (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM): EWSGI_REQUEST - do - Result := request_creator.item ([env, a_input]) - end - - new_response (req: EWSGI_REQUEST; a_output: EWSGI_OUTPUT_STREAM): EWSGI_RESPONSE_STREAM - do - Result := response_creator.item ([req, a_output]) - end - -invariant - callback_attached: callback /= Void - -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/ewsgi/specification/ewsgi_application.e b/library/server/ewsgi/specification/ewsgi_application.e index 958888e1..244a832c 100644 --- a/library/server/ewsgi/specification/ewsgi_application.e +++ b/library/server/ewsgi/specification/ewsgi_application.e @@ -8,73 +8,19 @@ note deferred class EWSGI_APPLICATION -feature -- Process request - - process (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM; a_output: EWSGI_OUTPUT_STREAM) - -- Process request with environment `env', and i/o streams `a_input' and `a_output' - local - rescued: BOOLEAN - req: detachable like new_request - res: detachable like new_response - do - if not rescued then - pre_execute (env) - req := new_request (env, a_input) - res := new_response (req, a_output) - execute (req, res) - post_execute (req, res) - else - rescue_execute (req, res, (create {EXCEPTION_MANAGER}).last_exception) - end - if res /= Void then - res.commit (a_output) - end - end - feature {NONE} -- Execution - execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) -- Execute the request -- See `req.input' for input stream -- `req.environment' for the Gateway environment - -- and `res.output' for output stream - deferred - end - - pre_execute (env: EWSGI_ENVIRONMENT) - -- Operation processed before `execute' + -- and `res' for the output buffer require - env_attached: env /= Void - do - end - - post_execute (req: detachable EWSGI_REQUEST; res: detachable EWSGI_RESPONSE_STREAM) - -- Operation processed after `execute', or after `rescue_execute' - do - end - - rescue_execute (req: detachable EWSGI_REQUEST; res: detachable EWSGI_RESPONSE_STREAM; a_exception: detachable EXCEPTION) - -- Operation processed on rescue of `execute' - do - post_execute (req, res) - end - -feature -- Factory - - new_request (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM): EWSGI_REQUEST - -- New Request context based on `env' and `a_input' - --| note: you can redefine this function to create your own - --| descendant of EWSGI_REQUEST , or even to reuse/recycle existing - --| instance of EWSGI_REQUEST - deferred - end - - new_response (req: EWSGI_REQUEST; a_output: EWSGI_OUTPUT_STREAM): EWSGI_RESPONSE_STREAM - -- New Response based on `req' and `a_output' - --| note: you can redefine this function to create your own - --| descendant of EWSGI_RESPONSE_STREAM , or even to reuse/recycle existing - --| instance of EWSGI_RESPONSE_STREAM + res_status_unset: not res.status_is_set deferred + ensure + res_status_set: res.status_is_set + res_committed: res.message_committed end note diff --git a/library/server/ewsgi/specification/request/ewsgi_request.e b/library/server/ewsgi/specification/request/ewsgi_request.e index f878adfc..d2190078 100644 --- a/library/server/ewsgi/specification/request/ewsgi_request.e +++ b/library/server/ewsgi/specification/request/ewsgi_request.e @@ -4,8 +4,6 @@ note You can create your own descendant of this class to add/remove specific value or processing - - This object is created by {GW_APPLICATION}.new_request_context ]" legal: "See notice at end of class." status: "See notice at end of class." diff --git a/library/server/ewsgi/specification/response/ewsgi_response_stream.e b/library/server/ewsgi/specification/response/ewsgi_response_buffer.e similarity index 60% rename from library/server/ewsgi/specification/response/ewsgi_response_stream.e rename to library/server/ewsgi/specification/response/ewsgi_response_buffer.e index ce06c852..3ce7a4b2 100644 --- a/library/server/ewsgi/specification/response/ewsgi_response_stream.e +++ b/library/server/ewsgi/specification/response/ewsgi_response_buffer.e @@ -1,19 +1,38 @@ note - description: "Summary description for {EWSGI_RESPONSE_STREAM}." + description: "Summary description for {EWSGI_RESPONSE_BUFFER}." author: "" date: "$Date$" revision: "$Revision$" deferred class - EWSGI_RESPONSE_STREAM + EWSGI_RESPONSE_BUFFER feature {EWSGI_APPLICATION} -- Commit - commit (a_output_stream: EWSGI_OUTPUT_STREAM) + commit -- Commit the current response deferred ensure - status_set: is_status_set + status_is_set: status_is_set + header_committed: header_committed + message_committed: message_committed + end + +feature -- Status report + + header_committed: BOOLEAN + -- Header committed? + deferred + end + + message_committed: BOOLEAN + -- Message committed? + deferred + end + + message_writable: BOOLEAN + -- Can message be written? + deferred end feature {NONE} -- Core output operation @@ -26,7 +45,7 @@ feature {NONE} -- Core output operation feature -- Status setting - is_status_set: BOOLEAN + status_is_set: BOOLEAN -- Is status set? deferred end @@ -35,11 +54,12 @@ feature -- Status setting -- Set response status code -- Should be done before sending any data back to the client require - status_not_set: not is_status_set + status_not_set: not status_is_set + header_not_committed: not header_committed deferred ensure status_code_set: status_code = a_code - status_set: is_status_set + status_set: status_is_set end status_code: INTEGER @@ -57,14 +77,21 @@ feature -- Output operation write_string (s: STRING) -- Send the string `s' require - status_set: is_status_set + message_writable: message_writable + deferred + end + + write_substring (s: STRING; a_begin_index, a_end_index: INTEGER) + -- Send the substring `s[a_begin_index:a_end_index]' + require + message_writable: message_writable deferred end write_file_content (fn: STRING) -- Send the content of file `fn' require - status_set: is_status_set + message_writable: message_writable deferred end @@ -73,10 +100,12 @@ feature -- Header output operation write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]]) -- Send headers with status `a_status', and headers from `a_headers' require - status_not_set: not is_status_set + status_not_set: not status_is_set + header_not_committed: not header_committed deferred ensure - status_set: is_status_set + header_committed: header_committed + status_set: status_is_set end note diff --git a/library/server/ewsgi/src/application/gw_agent_application.e b/library/server/ewsgi/src/application/ewsgi_agent_application.e similarity index 95% rename from library/server/ewsgi/src/application/gw_agent_application.e rename to library/server/ewsgi/src/application/ewsgi_agent_application.e index ec796f65..5b1a1deb 100644 --- a/library/server/ewsgi/src/application/gw_agent_application.e +++ b/library/server/ewsgi/src/application/ewsgi_agent_application.e @@ -5,10 +5,10 @@ note revision: "$Revision$" class - GW_AGENT_APPLICATION + EWSGI_AGENT_APPLICATION inherit - GW_APPLICATION_IMP + EWSGI_APPLICATION create make diff --git a/library/server/ewsgi/src/application/ewsgi_application.e b/library/server/ewsgi/src/application/ewsgi_application.e new file mode 100644 index 00000000..17044646 --- /dev/null +++ b/library/server/ewsgi/src/application/ewsgi_application.e @@ -0,0 +1,109 @@ +note + description: "[ + EWSGI_APPLICATION + ]" + specification: "EWSGI specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class + EWSGI_APPLICATION + +feature {NONE} -- Execution + + execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) + -- Execute the request + -- See `req.input' for input stream + -- `req.environment' for the Gateway environment + -- and `res' for output buffer + require + res_status_unset: not res.status_is_set + deferred + ensure + res_status_set: res.status_is_set + res_committed: res.message_committed + end + +feature -- Process request + + process (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM; a_output: EWSGI_OUTPUT_STREAM) + -- Process request with environment `env', and i/o streams `a_input' and `a_output' + local + rescued: BOOLEAN + req: detachable like new_request + res: detachable like new_response + do + if not rescued then + request_count := request_count + 1 + pre_execute (env) + req := new_request (env, a_input) + res := new_response (req, a_output) + execute (req, res) + post_execute (req, res) + else + rescue_execute (req, res, (create {EXCEPTION_MANAGER}).last_exception) + end + if res /= Void then + res.commit + end + end + +feature -- Access + + request_count: INTEGER + -- Request count + +feature {NONE} -- Execution + + pre_execute (env: EWSGI_ENVIRONMENT) + -- Operation processed before `execute' + require + env_attached: env /= Void + do + end + + post_execute (req: detachable EWSGI_REQUEST; res: detachable EWSGI_RESPONSE_BUFFER) + -- Operation processed after `execute', or after `rescue_execute' + do + end + + rescue_execute (req: detachable EWSGI_REQUEST; res: detachable EWSGI_RESPONSE_BUFFER; a_exception: detachable EXCEPTION) + -- Operation processed on rescue of `execute' + do + if + req /= Void and res /= Void + and a_exception /= Void and then attached a_exception.exception_trace as l_trace + then + res.write_header ({HTTP_STATUS_CODE}.internal_server_error, Void) + res.write_string ("
" + l_trace + "
") + end + post_execute (req, res) + end + +feature {NONE} -- Factory + + new_request (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM): EWSGI_REQUEST + do + create {EWSGI_REQUEST} Result.make (env, a_input) + Result.environment.set_variable (request_count.out, "REQUEST_COUNT") + end + + new_response (req: EWSGI_REQUEST; a_output: EWSGI_OUTPUT_STREAM): EWSGI_RESPONSE_BUFFER + do + create {EWSGI_RESPONSE_BUFFER} Result.make (a_output) + end + +;note + copyright: "2011-2011, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" + +end diff --git a/library/server/ewsgi/src/application/gw_application_imp.e b/library/server/ewsgi/src/application/gw_application_imp.e deleted file mode 100644 index 5bec3260..00000000 --- a/library/server/ewsgi/src/application/gw_application_imp.e +++ /dev/null @@ -1,69 +0,0 @@ -note - description: "Summary description for {GW_APPLICATION_IMP} " - legal: "See notice at end of class." - status: "See notice at end of class." - date: "$Date$" - revision: "$Revision$" - -deferred class - GW_APPLICATION_IMP - -inherit - EWSGI_APPLICATION - redefine - process, - rescue_execute - end - -feature -- Access - - request_count: INTEGER - -- Request count - -feature -- Execution - - process (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM; a_output: EWSGI_OUTPUT_STREAM) - -- Process request with environment `env', and i/o streams `a_input' and `a_output' - do - request_count := request_count + 1 - Precursor (env, a_input, a_output) - end - - rescue_execute (req: detachable EWSGI_REQUEST; res: detachable EWSGI_RESPONSE_STREAM; a_exception: detachable EXCEPTION) - -- Operation processed on rescue of `execute' - do - if - req /= Void and res /= Void - and a_exception /= Void and then attached a_exception.exception_trace as l_trace - then - res.write_header ({HTTP_STATUS_CODE}.internal_server_error, Void) - res.write_string ("
" + l_trace + "
") - end - Precursor (req, res, a_exception) - end - -feature -- Factory - - new_request (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM): EWSGI_REQUEST - do - create {GW_REQUEST_IMP} Result.make (env, a_input) - Result.execution_variables.set_variable (request_count.out, "REQUEST_COUNT") - end - - new_response (req: EWSGI_REQUEST; a_output: EWSGI_OUTPUT_STREAM): EWSGI_RESPONSE_STREAM - do - create {GW_RESPONSE_STREAM_IMP} Result.make (a_output) - end - -;note - copyright: "2011-2011, Eiffel Software and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" - source: "[ - Eiffel Software - 5949 Hollister Ave., Goleta, CA 93117 USA - Telephone 805-685-1006, Fax 805-685-6869 - Website http://www.eiffel.com - Customer support http://support.eiffel.com - ]" - -end diff --git a/library/server/ewsgi/src/connector/ewsgi_connector.e b/library/server/ewsgi/src/connector/ewsgi_connector.e new file mode 100644 index 00000000..1207ee9d --- /dev/null +++ b/library/server/ewsgi/src/connector/ewsgi_connector.e @@ -0,0 +1,44 @@ +note + description: "Summary description for {EWSGI_CONNECTOR}." + specification: "EWSGI/connector specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification" + date: "$Date$" + revision: "$Revision$" + +deferred class + EWSGI_CONNECTOR + +feature {NONE} -- Initialization + + make (a_app: like application) + do + application := a_app + initialize + end + + initialize + -- Initialize connector + do + end + +feature {NONE} -- Access + + application: EWSGI_APPLICATION + -- Gateway Application + +feature -- Server + + launch + deferred + end + +note + copyright: "2011-2011, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/server/ewsgi/src/response/gw_buffered_response.e b/library/server/ewsgi/src/extra/gw_buffered_response.e similarity index 54% rename from library/server/ewsgi/src/response/gw_buffered_response.e rename to library/server/ewsgi/src/extra/gw_buffered_response.e index e0178d13..241d33cf 100644 --- a/library/server/ewsgi/src/response/gw_buffered_response.e +++ b/library/server/ewsgi/src/extra/gw_buffered_response.e @@ -8,7 +8,14 @@ class GW_BUFFERED_RESPONSE inherit - EWSGI_RESPONSE_STREAM + EWSGI_RESPONSE_BUFFER + rename + make as buffer_make + redefine + write, + flush, + commit + end create {EWSGI_APPLICATION} make @@ -17,13 +24,11 @@ feature {NONE} -- Initialization make (a_output: like output; a_buffer_size: INTEGER) do - output := a_output + buffer_make (a_output) buffer_capacity := a_buffer_size create buffer.make (a_buffer_size) end - output: EWSGI_OUTPUT_STREAM - buffer: STRING_8 buffer_capacity: INTEGER @@ -60,25 +65,6 @@ feature {NONE} -- Core output operation end end -feature -- Status setting - - is_status_set: BOOLEAN - do - Result := status_code /= 0 - end - - set_status_code (a_code: INTEGER) - -- Set response status code - -- Should be done before sending any data back to the client - do - status_code := a_code - output.put_status_line (status_code) - --| We could also just append it to the `buffer' - end - - status_code: INTEGER - -- Response status - feature -- Output operation flush @@ -86,56 +72,6 @@ feature -- Output operation flush_buffer end - write_string (s: STRING) - -- Send the string `s' - do - write (s) - end - - write_file_content (fn: STRING) - -- Send the content of file `fn' - 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 (buffer_capacity) - write (f.last_string) - end - f.close - end - end - -feature -- Header output operation - - write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]]) - -- Send headers with status `a_status', and headers from `a_headers' - local - h: GW_HEADER - i,n: INTEGER - do - set_status_code (a_status_code) - create h.make - if a_headers /= Void then - from - i := a_headers.lower - n := a_headers.upper - until - i > n - loop - h.put_header_key_value (a_headers[i].key, a_headers[i].value) - i := i + 1 - end - end - write (h.string) - end - - feature {NONE} -- Implementation flush_buffer @@ -150,10 +86,10 @@ feature {NONE} -- Implementation feature {EWSGI_APPLICATION} -- Commit - commit (a_output: EWSGI_OUTPUT_STREAM) + commit do flush_buffer - a_output.flush + Precursor end ;note diff --git a/library/server/ewsgi/src/response/gw_in_memory_response.e b/library/server/ewsgi/src/extra/in_memory/gw_in_memory_response.e similarity index 70% rename from library/server/ewsgi/src/response/gw_in_memory_response.e rename to library/server/ewsgi/src/extra/in_memory/gw_in_memory_response.e index 7351a684..c8a68231 100644 --- a/library/server/ewsgi/src/response/gw_in_memory_response.e +++ b/library/server/ewsgi/src/extra/in_memory/gw_in_memory_response.e @@ -1,6 +1,5 @@ note description: "Summary description for {GW_IN_MEMORY_RESPONSE}." - author: "" date: "$Date$" revision: "$Revision$" @@ -8,9 +7,14 @@ class GW_IN_MEMORY_RESPONSE inherit - EWSGI_RESPONSE_STREAM + EWSGI_RESPONSE_BUFFER redefine - commit + make, + commit, + write, + set_status_code, + write_header, + flush end create {EWSGI_APPLICATION} @@ -18,8 +22,9 @@ create {EWSGI_APPLICATION} feature {NONE} -- Initialization - make + make (a_output: EWSGI_OUTPUT_STREAM) do + Precursor (a_output) create header.make create body.make (100) end @@ -38,11 +43,6 @@ feature {NONE} -- Status output feature -- Status setting - is_status_set: BOOLEAN - do - Result := status_code /= 0 - end - set_status_code (a_code: INTEGER) -- Set response status code -- Should be done before sending any data back to the client @@ -50,9 +50,6 @@ feature -- Status setting status_code := a_code end - status_code: INTEGER - -- Response status - feature -- Output operation flush @@ -60,31 +57,6 @@ feature -- Output operation --| Do nothing ... this is in_memory response end - write_string (s: STRING) - -- Send the string `s' - do - write (s) - end - - write_file_content (fn: STRING) - -- Send the content of file `fn' - 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) - write (f.last_string) - end - f.close - end - end - feature -- Header output operation write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]]) @@ -111,12 +83,17 @@ feature -- Header output operation feature {EWSGI_APPLICATION} -- Commit - commit (a_output: EWSGI_OUTPUT_STREAM) + commit + local + o: like output do - a_output.put_status_line (status_code) - a_output.put_string (header.string) - a_output.put_string (body) - a_output.flush + o := output + o.put_status_line (status_code) + o.put_string (header.string) + header_committed := True + o.put_string (body) + o.flush + Precursor end ;note diff --git a/library/server/ewsgi/src/application/gw_in_memory_response_application.e b/library/server/ewsgi/src/extra/in_memory/gw_in_memory_response_application.e similarity index 52% rename from library/server/ewsgi/src/application/gw_in_memory_response_application.e rename to library/server/ewsgi/src/extra/in_memory/gw_in_memory_response_application.e index cbe1f464..c1fcab1d 100644 --- a/library/server/ewsgi/src/application/gw_in_memory_response_application.e +++ b/library/server/ewsgi/src/extra/in_memory/gw_in_memory_response_application.e @@ -10,34 +10,14 @@ deferred class inherit EWSGI_APPLICATION redefine - process - end - -feature -- Access - - request_count: INTEGER - -- Request count - -feature -- Execution - - process (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM; a_output: EWSGI_OUTPUT_STREAM) - -- Process request with environment `env', and i/o streams `a_input' and `a_output' - do - request_count := request_count + 1 - Precursor (env, a_input, a_output) + new_response end feature -- Factory - new_request (env: EWSGI_ENVIRONMENT; a_input: EWSGI_INPUT_STREAM): EWSGI_REQUEST - do - create {GW_REQUEST_IMP} Result.make (env, a_input) - Result.execution_variables.set_variable (request_count.out, "REQUEST_COUNT") - end - new_response (req: EWSGI_REQUEST; a_output: EWSGI_OUTPUT_STREAM): GW_IN_MEMORY_RESPONSE do - create {GW_IN_MEMORY_RESPONSE} Result.make + create {GW_IN_MEMORY_RESPONSE} Result.make (a_output) end note diff --git a/library/server/ewsgi/examples/hello_world_with_response/src/framework/ewsgi_response.e b/library/server/ewsgi/src/extra/response_as_result/ewsgi_response.e similarity index 99% rename from library/server/ewsgi/examples/hello_world_with_response/src/framework/ewsgi_response.e rename to library/server/ewsgi/src/extra/response_as_result/ewsgi_response.e index a586880d..35aac77e 100644 --- a/library/server/ewsgi/examples/hello_world_with_response/src/framework/ewsgi_response.e +++ b/library/server/ewsgi/src/extra/response_as_result/ewsgi_response.e @@ -28,7 +28,7 @@ feature {NONE} -- Initialization feature {EWSGI_RESPONSE_APPLICATION} -- Response status - transmit_to (res: EWSGI_RESPONSE_STREAM) + transmit_to (res: EWSGI_RESPONSE_BUFFER) do res.set_status_code (status) res.write_string (headers) diff --git a/library/server/ewsgi/examples/hello_world_with_response/src/framework/ewsgi_response_application.e b/library/server/ewsgi/src/extra/response_as_result/ewsgi_response_application.e similarity index 96% rename from library/server/ewsgi/examples/hello_world_with_response/src/framework/ewsgi_response_application.e rename to library/server/ewsgi/src/extra/response_as_result/ewsgi_response_application.e index 58757f43..5c717e37 100644 --- a/library/server/ewsgi/examples/hello_world_with_response/src/framework/ewsgi_response_application.e +++ b/library/server/ewsgi/src/extra/response_as_result/ewsgi_response_application.e @@ -10,7 +10,7 @@ deferred class feature -- Execution - execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) -- Execute the request -- See `req.input' for input stream -- `req.environment' for the Gateway environment diff --git a/library/server/ewsgi/src/request/ewsgi_cookie.e b/library/server/ewsgi/src/request/ewsgi_cookie.e new file mode 100644 index 00000000..262ef4b6 --- /dev/null +++ b/library/server/ewsgi/src/request/ewsgi_cookie.e @@ -0,0 +1,76 @@ +note + description: "[ + Contains all information of a rfc2109 cookie that was read from the request header + ]" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + EWSGI_COOKIE + +create + make + +convert + value: {READABLE_STRING_8, STRING_8, READABLE_STRING_GENERAL, STRING_GENERAL} + +feature {NONE} -- Initialization + + make (a_name: STRING; a_value: STRING) + -- Creates current. + require + a_name_not_empty: a_name /= Void and then not a_name.is_empty + a_value_not_empty: a_value /= Void and then not a_value.is_empty + do + name := a_name + value := a_value + ensure + a_name_set: name = a_name + a_value_set: value = a_value + end + +feature -- Access + + name: STRING + -- Required. The name of the state information ("cookie") is NAME, + -- and its value is VALUE. NAMEs that begin with $ are reserved for + -- other uses and must not be used by applications. + + value: STRING + -- The VALUE is opaque to the user agent and may be anything the + -- origin server chooses to send, possibly in a server-selected + -- printable ASCII encoding. "Opaque" implies that the content is of + -- interest and relevance only to the origin server. The content + -- may, in fact, be readable by anyone that examines the Set-Cookie + -- header. + +feature -- Query + + variables: detachable HASH_TABLE [STRING, STRING] + -- Potential variable contained in the encoded cookie's value. + +feature -- Status report + + value_is_string (s: READABLE_STRING_GENERAL): BOOLEAN + -- Is `value' same string as `s' + do + Result := s.same_string (value) + end + +invariant + name_attached: name /= Void + value_attached: value /= Void + +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/ewsgi/src/request/ewsgi_environment.e b/library/server/ewsgi/src/request/ewsgi_environment.e new file mode 100644 index 00000000..cb19ebe2 --- /dev/null +++ b/library/server/ewsgi/src/request/ewsgi_environment.e @@ -0,0 +1,617 @@ +note + description: "[ + + Interface for a request environment + It includes CGI interface and a few extra values that are usually valuable + + See http://www.ietf.org/rfc/rfc3875 + + 2.2. Basic Rules + + The following rules are used throughout this specification to + describe basic parsing constructs. + + alpha = lowalpha | hialpha + lowalpha = a | b | c | d | e | f | g | h | + i | j | k | l | m | n | o | p | + q | r | s | t | u | v | w | x | + y | z + hialpha = A | B | C | D | E | F | G | H | + I | J | K | L | M | N | O | P | + Q | R | S | T | U | V | W | X | + Y | Z + digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | + 8 | 9 + alphanum = alpha | digit + OCTET = + CHAR = alpha | digit | separator | ! | # | $ | + %% | & | ' | * | + | - | . | ` | + ^ | _ | { | | | } | ~ | CTL + CTL = + SP = + HT = + NL = + LWSP = SP | HT | NL + separator = ( | ) | < | > | @ | , | ; | : | + \ | " | / | [ | ] | ? | = | { | + } | SP | HT + token = 1* + quoted-string = " *qdtext " + qdtext = + TEXT = + + 2.3. URL Encoding + + reserved = ; | / | ? | : | @ | & | = | + | $ | + , | [ | ] + + hex = digit | A | B | C | D | E | F | a | b + | c | d | e | f + escaped = "%%" hex hex + unreserved = alpha | digit | mark + mark = - | _ | . | ! | ~ | * | ' | ( | ) + + Note that newline (NL) need not be a single character, but can + be a character sequence. + + 3.2. The Script-URI + + script-URI = "://" ":" + "?" + + where is found from SERVER_PROTOCOL, , + and are the values of the respective + meta-variables. The SCRIPT_NAME and PATH_INFO values, URL-encoded + with ";", "=" and "?" reserved, give and . + + ]" + specification: "EWSGI specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred +class + EWSGI_ENVIRONMENT + +inherit + EWSGI_VARIABLES [STRING_8] + + ITERABLE [STRING_8] + +feature -- Access + + table: HASH_TABLE [STRING, STRING] + -- These variables are specific to requests made with HTTP. + -- Interpretation of these variables may depend on the value of + -- SERVER_PROTOCOL. + -- + -- Environment variables with names beginning with "HTTP_" contain + -- header data read from the client, if the protocol used was HTTP. + -- The HTTP header name is converted to upper case, has all + -- occurrences of "-" replaced with "_" and has "HTTP_" prepended to + -- give the environment variable name. The header data may be + -- presented as sent by the client, or may be rewritten in ways which + -- do not change its semantics. If multiple headers with the same + -- field-name are received then they must be rewritten as a single + -- header having the same semantics. Similarly, a header that is + -- received on more than one line must be merged onto a single line. + -- The server must, if necessary, change the representation of the + -- data (for example, the character set) to be appropriate for a CGI + -- environment variable. + -- + -- The server is not required to create environment variables for all + -- the headers that it receives. In particular, it may remove any + -- headers carrying authentication information, such as + -- "Authorization"; it may remove headers whose value is available to + -- the script via other variables, such as "Content-Length" and + -- "Content-Type". + -- + -- For convenience it might also include the following CGI entries + deferred + end + +feature -- Access: table + + new_cursor: HASH_TABLE_ITERATION_CURSOR [STRING_8, STRING_8] + -- Fresh cursor associated with current structure + do + create Result.make (table) + end + +feature -- Common Gateway Interface - 1.1 8 January 1996 + + auth_type: detachable STRING + -- This variable is specific to requests made via the "http" + -- scheme. + -- + -- If the Script-URI required access authentication for external + -- access, then the server MUST set the value of this variable + -- from the 'auth-scheme' token in the request's "Authorization" + -- header field. Otherwise it is set to NULL. + -- + -- AUTH_TYPE = "" | auth-scheme + -- auth-scheme = "Basic" | "Digest" | token + -- + -- HTTP access authentication schemes are described in section 11 + -- of the HTTP/1.1 specification [8]. The auth-scheme is not + -- case-sensitive. + -- + -- Servers MUST provide this metavariable to scripts if the + -- request header included an "Authorization" field that was + -- authenticated. + deferred + end + + content_length: detachable STRING + -- This metavariable is set to the size of the message-body + -- entity attached to the request, if any, in decimal number of + -- octets. If no data are attached, then this metavariable is + -- either NULL or not defined. The syntax is the same as for the + -- HTTP "Content-Length" header field (section 14.14, HTTP/1.1 + -- specification [8]). + -- + -- CONTENT_LENGTH = "" | 1*digit + -- + -- Servers MUST provide this metavariable to scripts if the + -- request was accompanied by a message-body entity. + deferred + end + + content_length_value: INTEGER + -- Integer value related to `content_length" + deferred + end + + content_type: detachable STRING + -- If the request includes a message-body, CONTENT_TYPE is set to + -- the Internet Media Type [9] of the attached entity if the type + -- was provided via a "Content-type" field in the request header, + -- or if the server can determine it in the absence of a supplied + -- "Content-type" field. The syntax is the same as for the HTTP + -- "Content-Type" header field. + -- + -- CONTENT_TYPE = "" | media-type + -- media-type = type "/" subtype *( ";" parameter) + -- type = token + -- subtype = token + -- parameter = attribute "=" value + -- attribute = token + -- value = token | quoted-string + -- + -- The type, subtype, and parameter attribute names are not + -- case-sensitive. Parameter values MAY be case sensitive. Media + -- types and their use in HTTP are described in section 3.7 of + -- the HTTP/1.1 specification [8]. + -- + -- Example: + -- + -- application/x-www-form-urlencoded + -- + -- There is no default value for this variable. If and only if it + -- is unset, then the script MAY attempt to determine the media + -- type from the data received. If the type remains unknown, then + -- the script MAY choose to either assume a content-type of + -- application/octet-stream or reject the request with a 415 + -- ("Unsupported Media Type") error. See section 7.2.1.3 for more + -- information about returning error status values. + -- + -- Servers MUST provide this metavariable to scripts if a + -- "Content-Type" field was present in the original request + -- header. If the server receives a request with an attached + -- entity but no "Content-Type" header field, it MAY attempt to + -- determine the correct datatype, or it MAY omit this + -- metavariable when communicating the request information to the + -- script. + deferred + end + + gateway_interface: STRING + -- This metavariable is set to the dialect of CGI being used by + -- the server to communicate with the script. Syntax: + -- + -- GATEWAY_INTERFACE = "CGI" "/" major "." minor + -- major = 1*digit + -- minor = 1*digit + -- + -- Note that the major and minor numbers are treated as separate + -- integers and hence each may be more than a single digit. Thus + -- CGI/2.4 is a lower version than CGI/2.13 which in turn is + -- lower than CGI/12.3. Leading zeros in either the major or the + -- minor number MUST be ignored by scripts and SHOULD NOT be + -- generated by servers. + -- + -- This document defines the 1.1 version of the CGI interface + -- ("CGI/1.1"). + -- + -- Servers MUST provide this metavariable to scripts. + -- + -- The version of the CGI specification to which this server + -- complies. Syntax: + -- + -- GATEWAY_INTERFACE = "CGI" "/" 1*digit "." 1*digit + -- + -- Note that the major and minor numbers are treated as separate + -- integers and that each may be incremented higher than a single + -- digit. Thus CGI/2.4 is a lower version than CGI/2.13 which in + -- turn is lower than CGI/12.3. Leading zeros must be ignored by + -- scripts and should never be generated by servers. + deferred + end + + path_info: STRING assign update_path_info + -- The PATH_INFO metavariable specifies a path to be interpreted + -- by the CGI script. It identifies the resource or sub-resource + -- to be returned by the CGI script, and it is derived from the + -- portion of the URI path following the script name but + -- preceding any query data. The syntax and semantics are similar + -- to a decoded HTTP URL 'path' token (defined in RFC 2396 [4]), + -- with the exception that a PATH_INFO of "/" represents a single + -- void path segment. + -- + -- PATH_INFO = "" | ( "/" path ) + -- path = segment *( "/" segment ) + -- segment = *pchar + -- pchar = + -- + -- The PATH_INFO string is the trailing part of the + -- component of the Script-URI (see section 3.2) that follows the + -- SCRIPT_NAME portion of the path. + -- + -- Servers MAY impose their own restrictions and limitations on + -- what values they will accept for PATH_INFO, and MAY reject or + -- edit any values they consider objectionable before passing + -- them to the script. + -- + -- Servers MUST make this URI component available to CGI scripts. + -- The PATH_INFO value is case-sensitive, and the server MUST + -- preserve the case of the PATH_INFO element of the URI when + -- making it available to scripts. + deferred + end + + path_translated: detachable STRING + -- PATH_TRANSLATED is derived by taking any path-info component + -- of the request URI (see section 6.1.6), decoding it (see + -- section 3.1), parsing it as a URI in its own right, and + -- performing any virtual-to-physical translation appropriate to + -- map it onto the server's document repository structure. If the + -- request URI includes no path-info component, the + -- PATH_TRANSLATED metavariable SHOULD NOT be defined. + -- + -- + -- PATH_TRANSLATED = *CHAR + -- + -- For a request such as the following: + -- + -- http://somehost.com/cgi-bin/somescript/this%2eis%2epath%2einfo + -- + -- the PATH_INFO component would be decoded, and the result + -- parsed as though it were a request for the following: + -- + -- http://somehost.com/this.is.the.path.info + -- + -- This would then be translated to a location in the server's + -- document repository, perhaps a filesystem path something like + -- this: + -- + -- /usr/local/www/htdocs/this.is.the.path.info + -- + -- The result of the translation is the value of PATH_TRANSLATED. + -- + -- The value of PATH_TRANSLATED may or may not map to a valid + -- repository location. Servers MUST preserve the case of the + -- path-info segment if and only if the underlying repository + -- supports case-sensitive names. If the repository is only + -- case-aware, case-preserving, or case-blind with regard to + -- document names, servers are not required to preserve the case + -- of the original segment through the translation. + -- + -- The translation algorithm the server uses to derive + -- PATH_TRANSLATED is implementation defined; CGI scripts which + -- use this variable may suffer limited portability. + -- + -- Servers SHOULD provide this metavariable to scripts if and + -- only if the request URI includes a path-info component. + deferred + end + + query_string: STRING + -- A URL-encoded string; the part of the Script-URI. (See + -- section 3.2.) + -- + -- QUERY_STRING = query-string + -- query-string = *uric + + -- The URL syntax for a query string is described in section 3 of + -- RFC 2396 [4]. + -- + -- Servers MUST supply this value to scripts. The QUERY_STRING + -- value is case-sensitive. If the Script-URI does not include a + -- query component, the QUERY_STRING metavariable MUST be defined + -- as an empty string (""). + deferred + end + + remote_addr: STRING + -- The IP address of the client sending the request to the + -- server. This is not necessarily that of the user agent (such + -- as if the request came through a proxy). + -- + -- REMOTE_ADDR = hostnumber + -- hostnumber = ipv4-address | ipv6-address + + -- The definitions of ipv4-address and ipv6-address are provided + -- in Appendix B of RFC 2373 [13]. + -- + -- Servers MUST supply this value to scripts. + deferred + end + + remote_host: detachable STRING + -- The fully qualified domain name of the client sending the + -- request to the server, if available, otherwise NULL. (See + -- section 6.1.9.) Fully qualified domain names take the form as + -- described in section 3.5 of RFC 1034 [10] and section 2.1 of + -- RFC 1123 [5]. Domain names are not case sensitive. + -- + -- Servers SHOULD provide this information to scripts. + deferred + end + + remote_ident: detachable STRING + -- The identity information reported about the connection by a + -- RFC 1413 [11] request to the remote agent, if available. + -- Servers MAY choose not to support this feature, or not to + -- request the data for efficiency reasons. + -- + -- REMOTE_IDENT = *CHAR + -- + -- The data returned may be used for authentication purposes, but + -- the level of trust reposed in them should be minimal. + -- + -- Servers MAY supply this information to scripts if the RFC1413 + -- [11] lookup is performed. + deferred + end + + remote_user: detachable STRING + -- If the request required authentication using the "Basic" + -- mechanism (i.e., the AUTH_TYPE metavariable is set to + -- "Basic"), then the value of the REMOTE_USER metavariable is + -- set to the user-ID supplied. In all other cases the value of + -- this metavariable is undefined. + -- + -- REMOTE_USER = *OCTET + -- + -- This variable is specific to requests made via the HTTP + -- protocol. + -- + -- Servers SHOULD provide this metavariable to scripts. + deferred + end + + request_method: STRING + -- The REQUEST_METHOD metavariable is set to the method with + -- which the request was made, as described in section 5.1.1 of + -- the HTTP/1.0 specification [3] and section 5.1.1 of the + -- HTTP/1.1 specification [8]. + -- + -- REQUEST_METHOD = http-method + -- http-method = "GET" | "HEAD" | "POST" | "PUT" | "DELETE" + -- | "OPTIONS" | "TRACE" | extension-method + -- extension-method = token + -- + -- The method is case sensitive. CGI/1.1 servers MAY choose to + -- process some methods directly rather than passing them to + -- scripts. + -- + -- This variable is specific to requests made with HTTP. + -- + -- Servers MUST provide this metavariable to scripts. + deferred + end + + script_name: STRING + -- The SCRIPT_NAME metavariable is set to a URL path that could + -- identify the CGI script (rather than the script's output). The + -- syntax and semantics are identical to a decoded HTTP URL + -- 'path' token (see RFC 2396 [4]). + -- + -- SCRIPT_NAME = "" | ( "/" [ path ] ) + -- + -- The SCRIPT_NAME string is some leading part of the + -- component of the Script-URI derived in some implementation + -- defined manner. No PATH_INFO or QUERY_STRING segments (see + -- sections 6.1.6 and 6.1.8) are included in the SCRIPT_NAME + -- value. + -- + -- Servers MUST provide this metavariable to scripts. + deferred + end + + server_name: STRING + -- The SERVER_NAME metavariable is set to the name of the server, + -- as derived from the part of the Script-URI (see section + -- 3.2). + -- + -- SERVER_NAME = hostname | hostnumber + -- + -- Servers MUST provide this metavariable to scripts. + deferred + end + + server_port: INTEGER + -- The SERVER_PORT metavariable is set to the port on which the + -- request was received, as used in the part of the + -- Script-URI. + -- + -- SERVER_PORT = 1*digit + -- + -- If the portion of the script-URI is blank, the actual + -- port number upon which the request was received MUST be + -- supplied. + -- + -- Servers MUST provide this metavariable to scripts. + deferred + end + + server_protocol: STRING + -- The SERVER_PROTOCOL metavariable is set to the name and + -- revision of the information protocol with which the request + -- arrived. This is not necessarily the same as the protocol + -- version used by the server in its response to the client. + -- + -- SERVER_PROTOCOL = HTTP-Version | extension-version + -- | extension-token + -- HTTP-Version = "HTTP" "/" 1*digit "." 1*digit + -- extension-version = protocol "/" 1*digit "." 1*digit + -- protocol = 1*( alpha | digit | "+" | "-" | "." ) + -- extension-token = token + -- + -- 'protocol' is a version of the part of the + -- Script-URI, but is not identical to it. For example, the + -- scheme of a request may be "https" while the protocol remains + -- "http". The protocol is not case sensitive, but by convention, + -- 'protocol' is in upper case. + -- + -- A well-known extension token value is "INCLUDED", which + -- signals that the current document is being included as part of + -- a composite document, rather than being the direct target of + -- the client request. + -- + -- Servers MUST provide this metavariable to scripts. + deferred + end + + server_software: STRING + -- The SERVER_SOFTWARE metavariable is set to the name and + -- version of the information server software answering the + -- request (and running the gateway). + -- + -- SERVER_SOFTWARE = 1*product + -- product = token [ "/" product-version ] + -- product-version = token + + -- Servers MUST provide this metavariable to scripts. + deferred + end + +feature -- HTTP_* + + http_accept: detachable STRING + -- Contents of the Accept: header from the current request, if there is one. + -- Example: 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' + deferred + end + + http_accept_charset: detachable STRING + -- Contents of the Accept-Charset: header from the current request, if there is one. + -- Example: 'iso-8859-1,*,utf-8'. + deferred + end + + http_accept_encoding: detachable STRING + -- Contents of the Accept-Encoding: header from the current request, if there is one. + -- Example: 'gzip'. + deferred + end + + http_accept_language: detachable STRING + -- Contents of the Accept-Language: header from the current request, if there is one. + -- Example: 'en'. + deferred + end + + http_connection: detachable STRING + -- Contents of the Connection: header from the current request, if there is one. + -- Example: 'Keep-Alive'. + deferred + end + + http_host: detachable STRING + -- Contents of the Host: header from the current request, if there is one. + deferred + end + + http_referer: detachable STRING + -- The address of the page (if any) which referred the user agent to the current page. + -- This is set by the user agent. + -- Not all user agents will set this, and some provide the ability to modify HTTP_REFERER as a feature. + -- In short, it cannot really be trusted. + deferred + end + + http_user_agent: detachable STRING + -- Contents of the User-Agent: header from the current request, if there is one. + -- This is a string denoting the user agent being which is accessing the page. + -- A typical example is: Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586). + -- Among other things, you can use this value to tailor your page's + -- output to the capabilities of the user agent. + deferred + end + + http_authorization: detachable STRING + -- Contents of the Authorization: header from the current request, if there is one. + deferred + end + +feature -- Extra + + request_uri: STRING + -- The URI which was given in order to access this page; for instance, '/index.html'. + deferred + end + + orig_path_info: detachable STRING + -- Original version of `path_info' before processed by Current environment + deferred + end + +feature {EWSGI_REQUEST} -- Element change + + set_orig_path_info (s: STRING) + -- Set ORIG_PATH_INFO to `s' + require + s_attached: s /= Void + deferred + ensure + same_orig_path_info: orig_path_info ~ variable ({EWSGI_ENVIRONMENT_NAMES}.orig_path_info) + end + + unset_orig_path_info + -- Unset ORIG_PATH_INFO + deferred + ensure + unset: not has_variable ({EWSGI_ENVIRONMENT_NAMES}.orig_path_info) + end + + update_path_info (a_path_info: like path_info) + -- Updated PATH_INFO + deferred + ensure + same_path_info: path_info ~ variable ({EWSGI_ENVIRONMENT_NAMES}.path_info) + end + +invariant + server_name_not_empty: not server_name.is_empty + server_port_set: server_port /= 0 + request_method_attached: request_method /= Void + path_info_attached: path_info /= Void + query_string_attached: query_string /= Void + remote_addr_attached: remote_addr /= Void + + path_info_identical: path_info ~ variable ({EWSGI_ENVIRONMENT_NAMES}.path_info) + +;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/ewsgi/src/request/ewsgi_environment_names.e b/library/server/ewsgi/src/request/ewsgi_environment_names.e new file mode 100644 index 00000000..46860b5d --- /dev/null +++ b/library/server/ewsgi/src/request/ewsgi_environment_names.e @@ -0,0 +1,89 @@ +note + description: "Summary description for {EWSGI_ENVIRONMENT_NAMES}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + EWSGI_ENVIRONMENT_NAMES + +feature -- Access + + request_uri: STRING = "REQUEST_URI" + + request_method: STRING = "REQUEST_METHOD" + + query_string: STRING = "QUERY_STRING" + + content_type: STRING = "CONTENT_TYPE" + + content_length: STRING = "CONTENT_LENGTH" + + path_info: STRING = "PATH_INFO" + + path_translated: STRING = "PATH_TRANSLATED" + + http_user_agent: STRING = "HTTP_USER_AGENT" + + http_authorization: STRING = "HTTP_AUTHORIZATION" + + http_host: STRING = "HTTP_HOST" + + http_cookie: STRING = "HTTP_COOKIE" + + http_from: STRING = "HTTP_FROM" + + http_accept: STRING = "HTTP_ACCEPT" + + http_accept_charset: STRING = "HTTP_ACCEPT_CHARSET" + + http_accept_encoding: STRING = "HTTP_ACCEPT_ENCODING" + + http_accept_language: STRING = "HTTP_ACCEPT_LANGUAGE" + + http_connection: STRING = "HTTP_CONNECTION" + + http_referer: STRING = "HTTP_REFERER" + + gateway_interface: STRING = "GATEWAY_INTERFACE" + + auth_type: STRING = "AUTH_TYPE" + + remote_host: STRING = "REMOTE_HOST" + + remote_addr: STRING = "REMOTE_ADDR" + + remote_ident: STRING = "REMOTE_IDENT" + + remote_user: STRING = "REMOTE_USER" + + script_name: STRING = "SCRIPT_NAME" + + server_name: STRING = "SERVER_NAME" + + server_port: STRING = "SERVER_PORT" + + server_protocol: STRING = "SERVER_PROTOCOL" + + server_software: STRING = "SERVER_SOFTWARE" + +feature -- Extra names + + request_time: STRING = "REQUEST_TIME" + + self: STRING = "SELF" + + orig_path_info: STRING = "ORIG_PATH_INFO" + +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/ewsgi/specification/implementation/ewsgi_environment_variables.e b/library/server/ewsgi/src/request/ewsgi_environment_variables.e similarity index 100% rename from library/server/ewsgi/specification/implementation/ewsgi_environment_variables.e rename to library/server/ewsgi/src/request/ewsgi_environment_variables.e diff --git a/library/server/ewsgi/src/request/gw_request_imp.e b/library/server/ewsgi/src/request/ewsgi_request.e similarity index 95% rename from library/server/ewsgi/src/request/gw_request_imp.e rename to library/server/ewsgi/src/request/ewsgi_request.e index 5407be96..3cecdcc7 100644 --- a/library/server/ewsgi/src/request/gw_request_imp.e +++ b/library/server/ewsgi/src/request/ewsgi_request.e @@ -1,21 +1,15 @@ note description: "[ - Server request context of the httpd request - - You can create your own descendant of this class to - add/remove specific value or processing - - This object is created by {EWSGI_APPLICATION}.new_request + EWSGI interface to represent the Request + ]" + specification: "EWSGI specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification" legal: "See notice at end of class." status: "See notice at end of class." date: "$Date$" revision: "$Revision$" class - GW_REQUEST_IMP - -inherit EWSGI_REQUEST create @@ -31,7 +25,6 @@ feature {NONE} -- Initialization input := a_input environment := env content_length := env.content_length_value - create execution_variables.make (10) create uploaded_files.make (0) raw_post_data_recorded := True @@ -100,6 +93,8 @@ feature -- Access: environment variables environment_variable (a_name: STRING): detachable STRING -- Environment variable related to `a_name' + require + a_name_valid: a_name /= Void and then not a_name.is_empty do Result := environment.variable (a_name) end @@ -107,20 +102,10 @@ feature -- Access: environment variables content_length: INTEGER -- Extracted Content-Length value -feature -- Access: execution variables - - execution_variables: GW_EXECUTION_VARIABLES - -- Execution variables set by the application - - execution_variable (a_name: STRING): detachable STRING_32 - -- Execution variable related to `a_name' - do - Result := execution_variables.variable (a_name) - end - feature -- URL parameters - parameters: GW_REQUEST_VARIABLES + parameters: EWSGI_REQUEST_VARIABLES + -- Variables extracted from QUERY_STRING local vars: like internal_parameters p,e: INTEGER @@ -155,13 +140,16 @@ feature -- URL parameters parameter (a_name: STRING): detachable STRING_32 -- Parameter for name `n'. + require + a_name_valid: a_name /= Void and then not a_name.is_empty do Result := parameters.variable (a_name) end feature -- Form fields and related - form_fields: GW_REQUEST_VARIABLES + form_fields: EWSGI_REQUEST_VARIABLES + -- Variables sent by POST request local vars: like internal_form_fields s: STRING @@ -198,6 +186,8 @@ feature -- Form fields and related form_field (a_name: STRING): detachable STRING_32 -- Field for name `a_name'. + require + a_name_valid: a_name /= Void and then not a_name.is_empty do Result := form_fields.variable (a_name) end @@ -244,6 +234,8 @@ feature -- Cookies cookies_variable (a_name: STRING): detachable STRING -- Field for name `a_name'. + require + a_name_valid: a_name /= Void and then not a_name.is_empty do Result := cookies_variables.item (a_name) end @@ -302,16 +294,6 @@ feature -- Access: global variable do create Result.make (100) - vars := execution_variables - from - vars.start - until - vars.after - loop - Result.put (vars.item_for_iteration, vars.key_for_iteration) - vars.forth - end - vars := environment.table from vars.start @@ -360,16 +342,13 @@ feature -- Access: global variable local s: detachable STRING_GENERAL do - s := execution_variable (a_name) + s := environment_variable (a_name) if s = Void then - s := environment_variable (a_name) + s := parameter (a_name) if s = Void then - s := parameter (a_name) + s := form_field (a_name) if s = Void then - s := form_field (a_name) - if s = Void then - s := cookies_variable (a_name) - end + s := cookies_variable (a_name) end end end @@ -378,7 +357,6 @@ feature -- Access: global variable end end - feature -- Access extra information request_time: detachable DATE_TIME @@ -392,6 +370,28 @@ feature -- Access extra information end end +feature -- Uploaded File Handling + + is_uploaded_file (a_filename: STRING): BOOLEAN + -- Is `a_filename' a file uploaded via HTTP Form + local + l_files: like uploaded_files + do + l_files := uploaded_files + if not l_files.is_empty then + from + l_files.start + until + l_files.after or Result + loop + if attached l_files.item_for_iteration.tmp_name as l_tmp_name and then l_tmp_name.same_string (a_filename) then + Result := True + end + l_files.forth + end + end + end + feature -- URL Utility absolute_script_url (a_path: STRING): STRING @@ -489,28 +489,6 @@ feature -- Element change end end -feature -- Uploaded File Handling - - is_uploaded_file (a_filename: STRING): BOOLEAN - -- Is `a_filename' a file uploaded via HTTP Form - local - l_files: like uploaded_files - do - l_files := uploaded_files - if not l_files.is_empty then - from - l_files.start - until - l_files.after or Result - loop - if attached l_files.item_for_iteration.tmp_name as l_tmp_name and then l_tmp_name.same_string (a_filename) then - Result := True - end - l_files.forth - end - end - end - feature {NONE} -- Temporary File handling delete_uploaded_file (uf: EWSGI_UPLOADED_FILE_DATA) diff --git a/library/server/ewsgi/src/request/gw_request_variables.e b/library/server/ewsgi/src/request/ewsgi_request_variables.e similarity index 99% rename from library/server/ewsgi/src/request/gw_request_variables.e rename to library/server/ewsgi/src/request/ewsgi_request_variables.e index 60aa3b54..236dc135 100644 --- a/library/server/ewsgi/src/request/gw_request_variables.e +++ b/library/server/ewsgi/src/request/ewsgi_request_variables.e @@ -8,7 +8,7 @@ note revision: "$Revision$" class - GW_REQUEST_VARIABLES + EWSGI_REQUEST_VARIABLES inherit EWSGI_VARIABLES [STRING_32] diff --git a/library/server/ewsgi/src/request/ewsgi_uploaded_file_data.e b/library/server/ewsgi/src/request/ewsgi_uploaded_file_data.e new file mode 100644 index 00000000..a8f2ea36 --- /dev/null +++ b/library/server/ewsgi/src/request/ewsgi_uploaded_file_data.e @@ -0,0 +1,103 @@ +note + description: "Summary description for {GW_UPLOADED_FILE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + EWSGI_UPLOADED_FILE_DATA + +create + make + +feature {NONE} -- Initialization + + make (n: like name; t: like content_type; s: like size) + do + name := n + content_type := t + size := s + end + +feature -- Access + + name: STRING + -- original filename + + content_type: STRING + -- Content type + + size: INTEGER + -- Size of uploaded file + + tmp_name: detachable STRING + -- Filename of tmp file + + tmp_basename: detachable STRING + -- Basename of tmp file + +feature -- Basic operation + + move_to (a_destination: STRING): BOOLEAN + -- Move current uploaded file to `a_destination' + require + has_no_error: not has_error + local + f: RAW_FILE + do + if attached tmp_name as n then + create f.make (n) + if f.exists then + f.change_name (a_destination) + Result := True + end + end + end + +feature -- Status + + has_error: BOOLEAN + -- Has error during uploading + do + Result := error /= 0 + end + + error: INTEGER + -- Eventual error code + --| no error => 0 + +feature -- Element change + + set_error (e: like error) + -- Set `error' to `e' + do + error := e + end + + set_tmp_name (n: like tmp_name) + -- Set `tmp_name' to `n' + do + tmp_name := n + end + + set_tmp_basename (n: like tmp_basename) + -- Set `tmp_basename' to `n' + do + tmp_basename := n + end + +invariant + + valid_tmp_name: not has_error implies attached tmp_name as n and then not n.is_empty + +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/ewsgi/src/request/ewsgi_variables.e b/library/server/ewsgi/src/request/ewsgi_variables.e new file mode 100644 index 00000000..048270e7 --- /dev/null +++ b/library/server/ewsgi/src/request/ewsgi_variables.e @@ -0,0 +1,80 @@ +note + description : "[ + Interface to access the variable stored in a container + ]" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class + EWSGI_VARIABLES [G -> STRING_GENERAL] + +inherit + ITERABLE [G] + +feature -- Status report + + has_variable (a_name: STRING): BOOLEAN + -- Has variable associated with `a_name' + require + a_name_not_empty: a_name /= Void and then not a_name.is_empty + deferred + end + +feature -- Access + + variable (a_name: STRING): detachable G + -- Value for variable associated with `a_name' + -- If not found, return Void + require + a_name_not_empty: a_name /= Void and then not a_name.is_empty + deferred + end + + variable_or_default (a_name: STRING; a_default: G; use_default_when_empty: BOOLEAN): G + -- Value for variable `a_name' + -- If not found, return `a_default' + require + a_name_not_empty: a_name /= Void and then not a_name.is_empty + do + if attached variable (a_name) as s then + if use_default_when_empty and then s.is_empty then + Result := a_default + else + Result := s + end + else + Result := a_default + end + end + +feature {EWSGI_REQUEST, EWSGI_APPLICATION, EWSGI_CONNECTOR} -- Element change + + set_variable (a_name: STRING; a_value: G) + require + a_name_not_empty: a_name /= Void and then not a_name.is_empty + deferred + ensure + variable_set: has_variable (a_name) and then variable (a_name) ~ a_value + end + + unset_variable (a_name: STRING) + require + a_name_not_empty: a_name /= Void and then not a_name.is_empty + deferred + ensure + variable_unset: not has_variable (a_name) + end + +note + copyright: "2011-2011, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/server/ewsgi/src/request/gw_execution_variables.e b/library/server/ewsgi/src/request/gw_execution_variables.e deleted file mode 100644 index 96e86770..00000000 --- a/library/server/ewsgi/src/request/gw_execution_variables.e +++ /dev/null @@ -1,56 +0,0 @@ -note - description: "Summary description for {GW_EXECUTION_VARIABLES}." - legal: "See notice at end of class." - status: "See notice at end of class." - date: "$Date$" - revision: "$Revision$" - -class - GW_EXECUTION_VARIABLES - -inherit - EWSGI_VARIABLES [STRING_32] - undefine - copy, is_equal - end - - HASH_TABLE [STRING_32, STRING] - -create - make - -feature -- Status report - - variable (a_name: STRING): detachable STRING_32 - do - Result := item (a_name) - end - - has_variable (a_name: STRING): BOOLEAN - do - Result := has (a_name) - end - -feature {EWSGI_REQUEST, EWSGI_APPLICATION, EWSGI_CONNECTOR} -- Element change - - set_variable (a_name: STRING; a_value: STRING_32) - do - force (a_value, a_name) - end - - unset_variable (a_name: STRING) - do - remove (a_name) - end - -note - copyright: "2011-2011, Eiffel Software and others" - license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" - source: "[ - Eiffel Software - 5949 Hollister Ave., Goleta, CA 93117 USA - Telephone 805-685-1006, Fax 805-685-6869 - Website http://www.eiffel.com - Customer support http://support.eiffel.com - ]" -end diff --git a/library/server/ewsgi/src/response/gw_response_stream_imp.e b/library/server/ewsgi/src/response/ewsgi_response_buffer.e similarity index 57% rename from library/server/ewsgi/src/response/gw_response_stream_imp.e rename to library/server/ewsgi/src/response/ewsgi_response_buffer.e index c345aadc..84a428d3 100644 --- a/library/server/ewsgi/src/response/gw_response_stream_imp.e +++ b/library/server/ewsgi/src/response/ewsgi_response_buffer.e @@ -1,14 +1,14 @@ note - description: "Summary description for {GW_RESPONSE_STREAM_IMP}." - author: "" + description: "[ + Response buffer + + ]" + specification: "EWSGI specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification" date: "$Date$" revision: "$Revision$" class - GW_RESPONSE_STREAM_IMP - -inherit - EWSGI_RESPONSE_STREAM + EWSGI_RESPONSE_BUFFER create {EWSGI_APPLICATION} make @@ -22,23 +22,44 @@ feature {NONE} -- Initialization feature {EWSGI_APPLICATION} -- Commit - commit (a_output_stream: EWSGI_OUTPUT_STREAM) + commit -- Commit the current response do - a_output_stream.flush + output.flush + message_committed := True + ensure + status_is_set: status_is_set + header_committed: header_committed + message_committed: message_committed + end + +feature -- Status report + + header_committed: BOOLEAN + -- Header committed? + + message_committed: BOOLEAN + -- Message committed? + + message_writable: BOOLEAN + -- Can message be written? + do + Result := status_is_set and header_committed end feature {NONE} -- Core output operation write (s: STRING) -- Send the content of `s' + -- this can be used for header and body do output.put_string (s) end feature -- Status setting - is_status_set: BOOLEAN + status_is_set: BOOLEAN + -- Is status set? do Result := status_code /= 0 end @@ -46,13 +67,19 @@ feature -- Status setting set_status_code (a_code: INTEGER) -- Set response status code -- Should be done before sending any data back to the client + require + status_not_set: not status_is_set + header_not_committed: not header_committed do status_code := a_code output.put_status_line (a_code) + ensure + status_code_set: status_code = a_code + status_set: status_is_set end status_code: INTEGER - -- Response status + -- Response status feature -- Output operation @@ -63,12 +90,25 @@ feature -- Output operation write_string (s: STRING) -- Send the string `s' + require + message_writable: message_writable do write (s) end + write_substring (s: STRING; start_index, end_index: INTEGER) + -- Send the substring `start_index:end_index]' + --| Could be optimized according to the target output + require + message_writable: message_writable + do + output.put_substring (s, start_index, end_index) + end + write_file_content (fn: STRING) -- Send the content of file `fn' + require + message_writable: message_writable do output.put_file_content (fn) end @@ -77,6 +117,9 @@ feature -- Header output operation write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]]) -- Send headers with status `a_status', and headers from `a_headers' + require + status_not_set: not status_is_set + header_not_committed: not header_committed local h: GW_HEADER i,n: INTEGER @@ -96,6 +139,10 @@ feature -- Header output operation end end write (h.string) + header_committed := True + ensure + status_set: status_is_set + header_committed: header_committed end feature {NONE} -- Implementation: Access diff --git a/library/server/ewsgi/src/stream/ewsgi_input_stream.e b/library/server/ewsgi/src/stream/ewsgi_input_stream.e new file mode 100644 index 00000000..7affe82f --- /dev/null +++ b/library/server/ewsgi/src/stream/ewsgi_input_stream.e @@ -0,0 +1,41 @@ +note + description : "[ + Objects that represents the input stream + ]" + specification: "EWSGI/connector specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class + EWSGI_INPUT_STREAM + +feature -- Access + + last_string: STRING_8 + -- Last read string from stream + deferred + end + +feature -- Basic operation + + read_stream (n: INTEGER) + require + n_positive: n > 0 + deferred + ensure + at_max_n: last_string.count <= n + end + +note + copyright: "2011-2011, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/server/ewsgi/src/stream/ewsgi_output_stream.e b/library/server/ewsgi/src/stream/ewsgi_output_stream.e new file mode 100644 index 00000000..ac2127ff --- /dev/null +++ b/library/server/ewsgi/src/stream/ewsgi_output_stream.e @@ -0,0 +1,84 @@ +note + description : "[ + Objects that represents the output stream + ]" + specification: "EWSGI/connector specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class + EWSGI_OUTPUT_STREAM + +feature -- Core operation + + put_string (s: STRING_8) + -- Write `s' into the output stream + require + s_not_empty: s /= Void and then not s.is_empty + deferred + end + + flush + -- Flush the output stream + do + end + +feature -- Status writing + + put_status_line (a_code: INTEGER) + -- Put status code line for `a_code' + --| Note this is a default implementation, and could be redefined + --| for instance in relation to NPH CGI script + deferred + end + +feature -- Basic operation + + put_substring (s: STRING; start_index, end_index: INTEGER) + -- Write `s[start_index:end_index]' into the output stream + --| Could be redefined for optimization + require + s_not_empty: s /= Void and then not s.is_empty + do + put_string (s.substring (start_index, end_index)) + end + + put_file_content (fn: STRING) + -- Send the content of file `fn' + 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 (4096) + put_string (f.last_string) + end + f.close + end + end + + put_header_line (s: STRING) + -- Send `s' to http client as header line + do + put_string (s) + put_string ("%R%N") + end + +note + copyright: "2011-2011, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/server/libfcgi/fcgi-safe.ecf b/library/server/libfcgi/fcgi-safe.ecf new file mode 100644 index 00000000..14784c26 --- /dev/null +++ b/library/server/libfcgi/fcgi-safe.ecf @@ -0,0 +1,52 @@ + + + + + + /\.svn$ + /\.git$ + /EIFGENs$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + /linux$ + /fake$ + + + + + + /windows$ + /fake$ + + + + + + + diff --git a/library/server/libfcgi/fcgi.ecf b/library/server/libfcgi/fcgi.ecf index 14784c26..d23bed18 100644 --- a/library/server/libfcgi/fcgi.ecf +++ b/library/server/libfcgi/fcgi.ecf @@ -8,7 +8,7 @@ /EIFGENs$
- @@ -30,7 +30,7 @@ - + diff --git a/library/server/libfcgi/tests/eiffelweb-safe.ecf b/library/server/libfcgi/tests/eiffelweb-safe.ecf new file mode 100644 index 00000000..31acd804 --- /dev/null +++ b/library/server/libfcgi/tests/eiffelweb-safe.ecf @@ -0,0 +1,16 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + + + + + + diff --git a/library/server/libfcgi/tests/eiffelweb.ecf b/library/server/libfcgi/tests/eiffelweb.ecf index 7ce530ca..35a36f2f 100644 --- a/library/server/libfcgi/tests/eiffelweb.ecf +++ b/library/server/libfcgi/tests/eiffelweb.ecf @@ -9,7 +9,7 @@ - +
diff --git a/library/server/request/router/router-safe.ecf b/library/server/request/router/router-safe.ecf index 5b6a5bf0..f655ffe4 100644 --- a/library/server/request/router/router-safe.ecf +++ b/library/server/request/router/router-safe.ecf @@ -12,10 +12,28 @@ - + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + +
diff --git a/library/server/request/router/router.ecf b/library/server/request/router/router.ecf index 7a90a5ab..88790095 100644 --- a/library/server/request/router/router.ecf +++ b/library/server/request/router/router.ecf @@ -12,9 +12,27 @@ - + - + + + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + diff --git a/library/server/request/router/src/application/routed_application.e b/library/server/request/router/src/application/routed_application.e index a50fdb5d..ffba0738 100644 --- a/library/server/request/router/src/application/routed_application.e +++ b/library/server/request/router/src/application/routed_application.e @@ -35,7 +35,7 @@ feature -- Setup feature -- Execution - execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) do if attached router.dispatch (req, res) as r then --| done @@ -44,7 +44,7 @@ feature -- Execution end end - execute_default (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + execute_default (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) deferred end diff --git a/library/server/request/router/src/handler/request_agent_handler.e b/library/server/request/router/src/handler/request_agent_handler.e index f95f0b8c..bcf9e475 100644 --- a/library/server/request/router/src/handler/request_agent_handler.e +++ b/library/server/request/router/src/handler/request_agent_handler.e @@ -23,11 +23,11 @@ feature -- Initialization feature -- Access - action: PROCEDURE [ANY, TUPLE [ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM]] + action: PROCEDURE [ANY, TUPLE [ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER]] feature -- Execution - execute_application (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + execute_application (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) do action.call ([ctx, req, res]) end diff --git a/library/server/request/router/src/handler/request_handler.e b/library/server/request/router/src/handler/request_handler.e index fe4525bd..7a72c8c5 100644 --- a/library/server/request/router/src/handler/request_handler.e +++ b/library/server/request/router/src/handler/request_handler.e @@ -29,7 +29,7 @@ feature -- Status report feature -- Execution - execute (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + execute (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) -- Execute request handler require is_valid_context: is_valid_context (req) @@ -52,7 +52,7 @@ feature -- Execution retry end - execute_method_not_allowed (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + execute_method_not_allowed (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) local s: STRING lst: LIST [STRING] @@ -75,7 +75,7 @@ feature -- Execution res.write_header ({HTTP_STATUS_CODE}.method_not_allowed, <<["Allow", s]>>) end - execute_application (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + execute_application (a_hdl_context: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) -- Execute request handler deferred end @@ -86,13 +86,13 @@ feature -- Execution --| To be redefined if needed end - post_execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + post_execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) -- Operation processed after `execute' do --| To be redefined if needed end - rescue_execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM) + rescue_execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) -- Operation processed after a rescue do --| To be redefined if needed diff --git a/library/server/request/router/src/router/request_router.e b/library/server/request/router/src/router/request_router.e index eef95ac6..acb22f4c 100644 --- a/library/server/request/router/src/router/request_router.e +++ b/library/server/request/router/src/router/request_router.e @@ -32,7 +32,7 @@ feature -- Registration feature -- Execution - dispatch (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_STREAM): detachable REQUEST_HANDLER + dispatch (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER): detachable REQUEST_HANDLER -- Dispatch `req, res' to the associated handler -- And return this handler -- If Result is Void, this means no handler was found.