diff --git a/library/server/ewsgi/connectors/httpd/dev/app_counter.e b/library/server/ewsgi/connectors/httpd/dev/app_counter.e new file mode 100644 index 00000000..1e4a6489 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/dev/app_counter.e @@ -0,0 +1,38 @@ +note + description: "Summary description for {APP_COUNTER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + APP_COUNTER + +create + put + +feature + + item: INTEGER + + put, replace (i: INTEGER) + do + item := i + end + + next_item: INTEGER + do + Result := item + 1 + item := Result + end + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/dev/app_wsf_execution.e b/library/server/ewsgi/connectors/httpd/dev/app_wsf_execution.e new file mode 100644 index 00000000..b4e817a5 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/dev/app_wsf_execution.e @@ -0,0 +1,56 @@ +note + description: "Summary description for {APP_WSF_EXECUTION}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + APP_WSF_EXECUTION + +inherit + WSF_EXECUTION + +create + make + +feature -- Execution + + execute + local + s: STRING + do + s := "Hello Concurrent EWF" + s.append (" (counter=") + s.append_integer (next_cell_counter_item (counter_cell)) + s.append (")%N") + + response.set_status_code (200) + response.put_header_line ("X-EWF-Dev: v1.0") + response.header.put_content_type_text_plain + response.header.put_content_length (s.count) + + response.put_string (s) + end + + next_cell_counter_item (cl: like counter_cell): INTEGER + do + Result := cl.next_item + end + + counter_cell: separate APP_COUNTER + once ("PROCESS") + create Result.put (0) + end + + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/dev/app_wsf_httpd_request_handler.e b/library/server/ewsgi/connectors/httpd/dev/app_wsf_httpd_request_handler.e new file mode 100644 index 00000000..e2c3c00f --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/dev/app_wsf_httpd_request_handler.e @@ -0,0 +1,36 @@ +note + description: "Summary description for {APP_WSF_HTTPD_REQUEST_HANDLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + APP_WSF_HTTPD_REQUEST_HANDLER + +inherit + WSF_HTTPD_REQUEST_HANDLER + +create + make + +feature -- Execute + + do_more (req: WGI_REQUEST; res: WGI_RESPONSE) + local + exec: WSF_EXECUTION + do + create {APP_WSF_EXECUTION} exec.make (req, res) + exec.execute + end + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/dev/httpd_connector_dev.e b/library/server/ewsgi/connectors/httpd/dev/httpd_connector_dev.e new file mode 100644 index 00000000..89bc7bea --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/dev/httpd_connector_dev.e @@ -0,0 +1,51 @@ +note + description: "[ + Objects that ... + ]" + author: "$Author$" + date: "$Date$" + revision: "$Revision$" + +class + HTTPD_CONNECTOR_DEV + +create + make + +feature {NONE} -- Initialization + + make + -- Initialize `Current'. + local + server: HTTPD_SERVER + fac: separate WSF_HTTPD_REQUEST_HANDLER_FACTORY [APP_WSF_EXECUTION] + do + print ("Hello%N") + create fac + create server.make (fac) + server.configuration.set_http_server_port (9090) + server.launch + end + +feature -- Status + +feature -- Access + +feature -- Change + +feature {NONE} -- Implementation + +invariant +-- invariant_clause: True + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/dev/wsf/wsf_httpd_request_handler.e b/library/server/ewsgi/connectors/httpd/dev/wsf/wsf_httpd_request_handler.e new file mode 100644 index 00000000..c5730b33 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/dev/wsf/wsf_httpd_request_handler.e @@ -0,0 +1,264 @@ +note + description: "Summary description for {WSF_HTTPD_REQUEST_HANDLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + WSF_HTTPD_REQUEST_HANDLER [G -> WSF_EXECUTION create make end] + +inherit + HTTPD_REQUEST_HANDLER + + WGI_EXPORTER + + REFACTORING_HELPER + +create + make + +feature -- Request processing + + process_request (a_socket: HTTPD_STREAM_SOCKET) + -- Process request ... + local + l_input: WGI_INPUT_STREAM + l_output: WGI_OUTPUT_STREAM + l_error: WGI_ERROR_STREAM + req: WGI_REQUEST_FROM_TABLE + res: detachable WGI_HTTPD_RESPONSE_STREAM + + exec: WSF_EXECUTION + retried: BOOLEAN + do + if not retried then + create {WGI_HTTPD_INPUT_STREAM} l_input.make (a_socket) + create {WGI_HTTPD_OUTPUT_STREAM} l_output.make (a_socket) + create {WGI_HTTPD_ERROR_STREAM} l_error.make_stderr (a_socket.descriptor.out) + + create req.make (httpd_environment (a_socket), l_input, Void) + create res.make (l_output, l_error) + + req.set_meta_string_variable ("RAW_HEADER_DATA", request_header) + + exec := new_execution (req, res) + exec.execute + res.push + else + end + rescue + if not retried then + retried := True + retry + end + end + + new_execution (req: WGI_REQUEST; res: WGI_RESPONSE): WSF_EXECUTION + do + create {G} Result.make (req, res) + end + + base: detachable READABLE_STRING_8 + do + --TODO + to_implement ("Base url support") + end + + httpd_environment (a_socket: HTTPD_STREAM_SOCKET): STRING_TABLE [READABLE_STRING_8] + local + p: INTEGER + l_request_uri, l_script_name, l_query_string, l_path_info: STRING + l_server_name, l_server_port: detachable STRING + l_headers_map: HASH_TABLE [STRING, STRING] + vn: STRING + + e: EXECUTION_ENVIRONMENT + enc: URL_ENCODER + utf: UTF_CONVERTER + do + l_request_uri := uri + l_headers_map := request_header_map + create e + create enc + if attached e.starting_environment as vars then + create Result.make_equal (vars.count) + across + vars as c + loop + Result.force (utf.utf_32_string_to_utf_8_string_8 (c.item), utf.utf_32_string_to_utf_8_string_8 (c.key)) + end + else + create Result.make (0) + end + + --| for Any Abc-Def-Ghi add (or replace) the HTTP_ABC_DEF_GHI variable to `Result' + from + l_headers_map.start + until + l_headers_map.after + loop + create vn.make_from_string (l_headers_map.key_for_iteration.as_upper) + vn.replace_substring_all ("-", "_") + if + vn.starts_with ("CONTENT_") and then + (vn.same_string_general ({WGI_META_NAMES}.content_type) or vn.same_string_general ({WGI_META_NAMES}.content_length)) + then + --| Keep this name + else + vn.prepend ("HTTP_") + end + add_environment_variable (l_headers_map.item_for_iteration, vn, Result) + l_headers_map.forth + end + + --| Specific cases + + p := l_request_uri.index_of ('?', 1) + if p > 0 then + l_script_name := l_request_uri.substring (1, p - 1) + l_query_string := l_request_uri.substring (p + 1, l_request_uri.count) + else + l_script_name := l_request_uri.string + l_query_string := "" + end + if attached l_headers_map.item ("Host") as l_host then + check has_host: Result.has ("HTTP_HOST") end +-- set_environment_variable (l_host, "HTTP_HOST", Result) + p := l_host.index_of (':', 1) + if p > 0 then + l_server_name := l_host.substring (1, p - 1) + l_server_port := l_host.substring (p+1, l_host.count) + else + l_server_name := l_host + l_server_port := "80" -- Default + end + else + check host_available: False end + end + + if attached l_headers_map.item ("Authorization") as l_authorization then + check has_authorization: Result.has ("HTTP_AUTHORIZATION") end +-- set_environment_variable (l_authorization, "HTTP_AUTHORIZATION", Result) + p := l_authorization.index_of (' ', 1) + if p > 0 then + set_environment_variable (l_authorization.substring (1, p - 1), "AUTH_TYPE", Result) + end + end + + set_environment_variable ("CGI/1.1", "GATEWAY_INTERFACE", Result) + set_environment_variable (l_query_string, "QUERY_STRING", Result) + + if attached remote_info as l_remote_info then + set_environment_variable (l_remote_info.addr, "REMOTE_ADDR", Result) + set_environment_variable (l_remote_info.hostname, "REMOTE_HOST", Result) + set_environment_variable (l_remote_info.port.out, "REMOTE_PORT", Result) +-- set_environment_variable (Void, "REMOTE_IDENT", Result) +-- set_environment_variable (Void, "REMOTE_USER", Result) + end + + set_environment_variable (l_request_uri, "REQUEST_URI", Result) + set_environment_variable (method, "REQUEST_METHOD", Result) + + set_environment_variable (l_script_name, "SCRIPT_NAME", Result) + set_environment_variable (l_server_name, "SERVER_NAME", Result) + set_environment_variable (l_server_port, "SERVER_PORT", Result) + set_environment_variable (version, "SERVER_PROTOCOL", Result) + set_environment_variable ({HTTPD_CONFIGURATION}.Server_details, "SERVER_SOFTWARE", Result) + + --| Apply `base' value + if attached base as l_base and then l_request_uri /= Void then + if l_request_uri.starts_with (l_base) then + l_path_info := l_request_uri.substring (l_base.count + 1, l_request_uri.count) + p := l_path_info.index_of ('?', 1) + if p > 0 then + l_path_info.keep_head (p - 1) + end + Result.force (l_base, "SCRIPT_NAME") + else + -- This should not happen, this means the `base' is not correctly set. + -- It is better to consider base as empty, rather than having empty PATH_INFO + check valid_base_value: False end + + l_path_info := l_request_uri + p := l_request_uri.index_of ('?', 1) + if p > 0 then + l_path_info := l_request_uri.substring (1, p - 1) + else + l_path_info := l_request_uri.string + end + Result.force ("", "SCRIPT_NAME") + end + --| In order to have same path value for PATH_INFO on various connectors and servers + --| the multiple slashes must be stripped to single slash. + --| tested with: CGI+apache, libfcgi+apache on Windows and Linux + --| + --| For example: "////abc/def///end////" to "/abc/def/end/" ? + convert_multiple_slashes_to_single (l_path_info) + Result.force (enc.decoded_utf_8_string (l_path_info), "PATH_INFO") + end + end + + add_environment_variable (a_value: detachable STRING; a_var_name: READABLE_STRING_GENERAL; env: STRING_TABLE [READABLE_STRING_8]) + -- Add variable `a_var_name => a_value' to `env' + do + if a_value /= Void then + if env.has_key (a_var_name) and then attached env.found_item as l_existing_value then + --| Check http://www.ietf.org/rfc/rfc3875 4.1.18 + check find_proper_rewrite_for_same_header: False end + env.force (l_existing_value + " " + a_value, a_var_name) + else + env.force (a_value, a_var_name) + end + end + end + + set_environment_variable (a_value: detachable STRING; a_var_name: READABLE_STRING_GENERAL; env: STRING_TABLE [READABLE_STRING_8]) + -- Add variable `a_var_name => a_value' to `env' + do + if a_value /= Void then + env.force (a_value, a_var_name) + end + end + +feature {NONE} -- Implementation + + convert_multiple_slashes_to_single (s: STRING_8) + -- Replace multiple slashes sequence by a single slash character. + local + i,n: INTEGER + do + from + i := 1 + n := s.count + until + i > n + loop + if s[i] = '/' then + -- Remove following slashes '/'. + from + i := i + 1 + until + i > n or s[i] /= '/' + loop + s.remove (i) + n := n - 1 + end + else + i := i + 1 + end + end + end + + + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/dev/wsf/wsf_httpd_request_handler_factory.e b/library/server/ewsgi/connectors/httpd/dev/wsf/wsf_httpd_request_handler_factory.e new file mode 100644 index 00000000..94b667f4 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/dev/wsf/wsf_httpd_request_handler_factory.e @@ -0,0 +1,30 @@ +note + description: "Summary description for {WSF_HTTPD_REQUEST_HANDLER_FACTORY}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + WSF_HTTPD_REQUEST_HANDLER_FACTORY [G -> WSF_EXECUTION create make end] + +inherit + HTTPD_REQUEST_HANDLER_FACTORY + +feature -- Factory + + new_handler: separate WSF_HTTPD_REQUEST_HANDLER [G] + do + create Result.make + end + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/httpd-safe.ecf b/library/server/ewsgi/connectors/httpd/httpd-safe.ecf new file mode 100644 index 00000000..38f010d3 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/httpd-safe.ecf @@ -0,0 +1,74 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + + + + + + + + + + + + + + + + + + + + + /ssl$ + /EIFGENs$ + /no_ssl$ + /concurrency$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /httpd$ + + + + + + + + + diff --git a/library/server/ewsgi/connectors/httpd/license.lic b/library/server/ewsgi/connectors/httpd/license.lic new file mode 100644 index 00000000..d4d72876 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/license.lic @@ -0,0 +1,10 @@ +${NOTE_KEYWORD} + copyright: "2011-${YEAR}, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" diff --git a/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/none/httpd_connection_handler.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/none/httpd_connection_handler.e new file mode 100644 index 00000000..79ae936a --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/none/httpd_connection_handler.e @@ -0,0 +1,101 @@ +note + description: "Summary description for {HTTPD_CONNECTION_HANDLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + HTTPD_CONNECTION_HANDLER + +inherit + HTTPD_CONNECTION_HANDLER_I + +create + make + +feature {NONE} -- Initialization + + initialize + do + end + +feature -- Access + + is_shutdown_requested: BOOLEAN + + shutdown_requested (a_server: like server): BOOLEAN + do + -- FIXME: we should probably remove this possibility, check with EWF if this is needed. + Result := a_server.controller.shutdown_requested + end + +feature -- Execution + + process_incoming_connection (a_socket: HTTPD_STREAM_SOCKET) + -- Process incoming connection + -- note that the precondition matters for scoop synchronization. + do + is_shutdown_requested := is_shutdown_requested or shutdown_requested (server) + if is_shutdown_requested then + a_socket.cleanup + elseif attached server.factory.new_handler as h then + process_connection_handler (h, a_socket) + else + check is_not_full: False end + a_socket.cleanup + end + update_is_shutdown_requested + end + + process_connection_handler (hdl: HTTPD_REQUEST_HANDLER; a_socket: HTTPD_STREAM_SOCKET) + require + not hdl.has_error + do + --| FIXME jfiat [2011/11/03] : should use a Pool of Threads/Handler to process this connection + --| also handle permanent connection...? + + hdl.set_client_socket (a_socket) + if not hdl.has_error then +-- hdl.set_logger (server) + hdl.execute + else + log ("Internal error (set_client_socket failed)") + end + rescue + log ("Releasing handler after exception!") + hdl.release + a_socket.cleanup + end + + update_is_shutdown_requested + do + is_shutdown_requested := shutdown_requested (server) + end + + shutdown + do + if not is_shutdown_requested then + is_shutdown_requested := True + server.controller.shutdown + end + end + +feature {HTTPD_SERVER_I} -- Status report + + wait_for_completion + -- Wait until Current is ready for shutdown + do + -- no concurrency, then current task should be done. + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/concurrency/none/httpd_request_handler.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/none/httpd_request_handler.e new file mode 100644 index 00000000..1e607c5e --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/none/httpd_request_handler.e @@ -0,0 +1,30 @@ +note + description : "Concurrent specific feature to implemente HTTPD_REQUEST_HANDLER" + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +deferred class + HTTPD_REQUEST_HANDLER + +inherit + HTTPD_REQUEST_HANDLER_I + +feature -- Change + + set_client_socket (a_socket: HTTPD_STREAM_SOCKET) + do + client_socket := a_socket + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/concurrency/none/httpd_request_handler_factory.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/none/httpd_request_handler_factory.e new file mode 100644 index 00000000..8bff0870 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/none/httpd_request_handler_factory.e @@ -0,0 +1,16 @@ +note + description: "Summary description for {HTTPD_REQUEST_HANDLER_FACTORY}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTPD_REQUEST_HANDLER_FACTORY + +inherit + HTTPD_REQUEST_HANDLER_FACTORY_I + +note + copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/httpd_connection_handler.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/httpd_connection_handler.e new file mode 100644 index 00000000..fcf5c3d7 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/httpd_connection_handler.e @@ -0,0 +1,160 @@ +note + description: "Summary description for {HTTPD_CONNECTION_HANDLER}." + date: "$Date$" + revision: "$Revision$" + +class + HTTPD_CONNECTION_HANDLER + +inherit + HTTPD_CONNECTION_HANDLER_I + redefine + initialize + end + +create + make + +feature {NONE} -- Initialization + + initialize + local + n: INTEGER + p: like pool + do + n := max_concurrent_connections (server) + create p.make (n) + initialize_pool (p, n) + pool := p + end + + initialize_pool (p: like pool; n: INTEGER) + do + p.set_count (n) + end + +feature -- Access + + is_shutdown_requested: BOOLEAN + + max_concurrent_connections (a_server: like server): INTEGER + do + Result := a_server.configuration.max_concurrent_connections + end + +feature {HTTPD_SERVER_I} -- Execution + + shutdown + do + if not is_shutdown_requested then + is_shutdown_requested := True + pool_gracefull_stop (pool) + end + end + + pool_gracefull_stop (p: like pool) + do + p.gracefull_stop + end + + process_incoming_connection (a_socket: HTTPD_STREAM_SOCKET) + -- + do + debug ("dbglog") + dbglog (generator + ".before process_incoming_connection {"+ a_socket.descriptor.out +"} -- SCOOP WAIT!") + end +-- if is_shutdown_requested then +-- a_socket.cleanup +-- else + process_connection_on_pool (a_socket, pool) -- Wait on not pool.is_full or is_stop_requested +-- end + debug ("dbglog") + dbglog (generator + ".after process_incoming_connection {"+ a_socket.descriptor.out +"}") + end + end + + process_connection_on_pool (a_socket: HTTPD_STREAM_SOCKET; a_pool: like pool) + -- Process incoming connection + -- note that the precondition matters for scoop synchronization. + require + concurrency: not a_pool.is_full or is_shutdown_requested or a_pool.stop_requested + do + debug ("dbglog") + dbglog (generator + ".ENTER process_connection {"+ a_socket.descriptor.out +"}") + end + if is_shutdown_requested then + a_socket.cleanup + elseif attached a_pool.separate_item (factory) as h then + process_request_handler (h, a_socket) + else + check is_not_full: False end + a_socket.cleanup + end + debug ("dbglog") + dbglog (generator + ".LEAVE process_connection {"+ a_socket.descriptor.out +"}") + end + end + + process_request_handler (hdl: separate HTTPD_REQUEST_HANDLER; a_socket: HTTPD_STREAM_SOCKET) + require + not hdl.has_error + do + --| FIXME jfiat [2011/11/03] : should use a Pool of Threads/Handler to process this connection + --| also handle permanent connection...? + + debug ("dbglog") + dbglog (generator + ".ENTER process_request_handler {"+ a_socket.descriptor.out +"}") + end + + hdl.set_client_socket (a_socket) + if hdl.has_error then + log ("Internal error (set_client_socket failed)") + else +-- hdl.set_logger (server) + if attached hdl.separate_execution as l_result then + end + hdl.separate_release + end + debug ("dbglog") + dbglog (generator + ".LEAVE process_request_handler {"+ a_socket.descriptor.out +"}") + end + rescue + log ("Releasing handler after exception!") + hdl.separate_release +-- a_socket.cleanup + end + +feature {HTTPD_SERVER_I} -- Status report + + wait_for_completion + -- Wait until Current is ready for shutdown + do + wait_for_pool_completion (pool) + end + + wait_for_pool_completion (p: like pool) + require + p.is_empty + do + p.terminate + end + +feature {NONE} -- Access + + pool: separate CONCURRENT_POOL [HTTPD_REQUEST_HANDLER] + -- Pool of separate connection handlers. + +invariant + pool_attached: pool /= Void + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/concurrency/scoop/httpd_request_handler.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/httpd_request_handler.e new file mode 100644 index 00000000..2c5754fc --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/httpd_request_handler.e @@ -0,0 +1,111 @@ +note + description: "[ + Instance of HTTPD_REQUEST_HANDLER will process the incoming connection + and extract information on the request and the server + ]" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTPD_REQUEST_HANDLER + +inherit + HTTPD_REQUEST_HANDLER_I + redefine +-- set_client_socket, + release, + reset + end + + CONCURRENT_POOL_ITEM + rename + release as release_pool_item + end + +feature {NONE} -- Initialization + + reset + do + if attached client_socket_source as l_sock then + cleanup_separate_socket (l_sock) + end + Precursor + client_socket_source := Void + end + + cleanup_separate_socket (a_socket: attached like client_socket_source) + do + a_socket.cleanup + end + +feature -- Access + + client_socket: detachable HTTPD_STREAM_SOCKET + + client_socket_source: detachable separate HTTPD_STREAM_SOCKET + -- Associated original client socket + -- kept to avoid being closed when disposed, + -- and thus avoid closing related `client_socket'. + +feature -- Change + + set_client_socket (a_socket: separate HTTPD_STREAM_SOCKET) + local + retried: BOOLEAN + do + if retried then + has_error := True + else + create client_socket.make_from_separate (a_socket) + client_socket_source := a_socket + end + rescue + retried := True + retry + end + +feature -- Execution + + separate_execution: BOOLEAN + do + if attached client_socket as s then + execute (s) + end + Result := True + end + +feature {CONCURRENT_POOL, HTTPD_CONNECTION_HANDLER_I} -- Basic operation + + separate_release + do + if attached client_socket as s then + release (s) + end + end + + release (a_socket: HTTPD_STREAM_SOCKET) + local + d: STRING + do + d := a_socket.descriptor.out + debug ("dbglog") + dbglog (generator + ".release: ENTER {" + d + "}") + end + Precursor {HTTPD_REQUEST_HANDLER_I} (a_socket) + release_pool_item + debug ("dbglog") + dbglog (generator + ".release: LEAVE {" + d + "}") + end + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/concurrency/scoop/httpd_request_handler_factory.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/httpd_request_handler_factory.e new file mode 100644 index 00000000..843fc8e3 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/httpd_request_handler_factory.e @@ -0,0 +1,21 @@ +note + description: "Summary description for {HTTPD_REQUEST_HANDLER_FACTORY}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTPD_REQUEST_HANDLER_FACTORY + +inherit + HTTPD_REQUEST_HANDLER_FACTORY_I + + CONCURRENT_POOL_FACTORY [HTTPD_REQUEST_HANDLER] + rename + new_separate_item as new_handler + end + +note + copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/pool/concurrent_pool.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/pool/concurrent_pool.e new file mode 100644 index 00000000..0bead25a --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/pool/concurrent_pool.e @@ -0,0 +1,181 @@ +note + description: "Summary description for {CONCURRENT_POOL}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + CONCURRENT_POOL [G -> CONCURRENT_POOL_ITEM] + +inherit + HTTPD_DEBUG_FACILITIES + +create + make + +feature {NONE} -- Initialization + + make (n: INTEGER) + do + capacity := n + create items.make_empty (n) +-- create busy_items.make_filled (False, n) + create busy_items.make_empty (n) + end + +feature -- Access + + count: INTEGER + + is_full: BOOLEAN + do + Result := count >= capacity + end + + is_empty: BOOLEAN + do + Result := count = 0 + end + + capacity: INTEGER + + stop_requested: BOOLEAN + +feature -- Access + + separate_item (a_factory: separate CONCURRENT_POOL_FACTORY [G]): detachable separate G + require + is_not_full: not is_full + local + i,n,pos: INTEGER + lst: like busy_items + l_item: detachable separate G + do + if not stop_requested then + from + lst := busy_items + pos := -1 + i := 0 + n := lst.count - 1 + until + i > n or l_item /= Void or pos >= 0 + loop + if not lst [i] then -- is free (i.e not busy) + pos := i + + if items.valid_index (pos) then + l_item := items [pos] + if l_item /= Void then + busy_items [pos] := True + end + end + if l_item = Void then + -- Empty, then let's create one. + l_item := a_factory.new_separate_item + register_item (l_item) + items [pos] := l_item + end + end + i := i + 1 + end + if l_item = Void then + -- Pool is FULL ... + check overcapacity: False end + else + debug ("pool", "dbglog") + dbglog ("Lock pool item #" + pos.out + " (free:"+ (capacity - count).out +"))") + end + count := count + 1 + busy_items [pos] := True + Result := l_item + end + end + end + +feature -- Basic operation + + gracefull_stop + do + stop_requested := True + end + +feature {NONE} -- Internal + + items: SPECIAL [detachable separate G] + + busy_items: SPECIAL [BOOLEAN] + +feature {CONCURRENT_POOL_ITEM} -- Change + + release_item (a_item: separate G) + -- Unregister `a_item' from Current pool. + require + count > 0 + local + i,n,pos: INTEGER + lst: like items + do + -- release handler for reuse + from + lst := items + i := 0 + n := lst.count - 1 + until + i > n or lst [i] = a_item + loop + i := i + 1 + end + if i <= n then + pos := i + busy_items [pos] := False + count := count - 1 +--reuse items [pos] := Void + debug ("pool", "dbglog") + dbglog ("Released pool item #" + i.out + " (free:"+ (capacity - count).out +"))") + end + else + check known_item: False end + end + end + +feature -- Change + + set_count (n: INTEGER) + local + g: detachable separate G + do + capacity := n + items.fill_with (g, 0, n - 1) + busy_items.fill_with (False, 0, n - 1) + end + + terminate + local + l_items: like items + do + l_items := items + l_items.wipe_out + end + +feature {NONE} -- Implementation + +-- new_separate_item: separate G +-- deferred +-- end + + register_item (a_item: separate G) + do + a_item.set_pool (Current) + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/concurrency/scoop/pool/concurrent_pool_factory.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/pool/concurrent_pool_factory.e new file mode 100644 index 00000000..497fbb69 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/pool/concurrent_pool_factory.e @@ -0,0 +1,19 @@ +note + description: "Summary description for {CONCURRENT_POOL_FACTORY}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CONCURRENT_POOL_FACTORY [G -> CONCURRENT_POOL_ITEM] + +feature -- Access + + new_separate_item: separate G + deferred + end + +note + copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/pool/concurrent_pool_item.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/pool/concurrent_pool_item.e new file mode 100644 index 00000000..c197fd88 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/scoop/pool/concurrent_pool_item.e @@ -0,0 +1,47 @@ +note + description: "Summary description for {CONCURRENT_POOL_ITEM}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + CONCURRENT_POOL_ITEM + +feature {NONE} -- Access + + pool: detachable separate CONCURRENT_POOL [CONCURRENT_POOL_ITEM] + +feature {CONCURRENT_POOL} -- Change + + set_pool (p: like pool) + do + pool := p + end + +feature {CONCURRENT_POOL, HTTPD_CONNECTION_HANDLER_I} -- Basic operation + + release + do + if attached pool as p then + pool_release (p) + end + end + +feature {NONE} -- Implementation + + pool_release (p: separate CONCURRENT_POOL [CONCURRENT_POOL_ITEM]) + do + p.release_item (Current) + end + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/concurrency/thread/httpd_connection_handler.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/thread/httpd_connection_handler.e new file mode 100644 index 00000000..d3d1354e --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/thread/httpd_connection_handler.e @@ -0,0 +1,111 @@ +note + description: "Summary description for {HTTPD_CONNECTION_HANDLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + HTTPD_CONNECTION_HANDLER + +inherit + HTTPD_CONNECTION_HANDLER_I + redefine + initialize + end + +create + make + +feature {NONE} -- Initialization + + initialize + local + n: INTEGER + do + n := max_concurrent_connections (server) + create pool.make (n.to_natural_32) + end + +feature -- Access + + is_shutdown_requested: BOOLEAN + + max_concurrent_connections (a_server: like server): INTEGER + do + Result := a_server.configuration.max_concurrent_connections + end + +feature {HTTPD_SERVER_I} -- Execution + + shutdown + do + if not is_shutdown_requested then + is_shutdown_requested := True + pool_gracefull_stop (pool) + end + end + + pool_gracefull_stop (p: like pool) + do + p.terminate + end + + process_incoming_connection (a_socket: HTTPD_STREAM_SOCKET) + do + if is_shutdown_requested then + a_socket.cleanup + else + process_connection_handler (factory.new_handler, a_socket) + end + end + + process_connection_handler (hdl: separate HTTPD_REQUEST_HANDLER; a_socket: HTTPD_STREAM_SOCKET) + require + not hdl.has_error + do + debug ("dbglog") + dbglog (generator + ".ENTER process_connection_handler {"+ a_socket.descriptor.out +"}") + end + if not hdl.has_error then +-- hdl.set_logger (server) +-- hdl.set_client_socket (a_socket) + pool.add_work (agent hdl.execute (a_socket)) + else + log ("Internal error (set_client_socket failed)") + end + debug ("dbglog") + dbglog (generator + ".LEAVE process_connection_handler {"+ a_socket.descriptor.out +"}") + end + rescue + log ("Releasing handler after exception!") + hdl.release (a_socket) +-- hdl.client_socket.cleanup + end + +feature {HTTPD_SERVER_I} -- Status report + + wait_for_completion + -- Wait until Current is ready for shutdown + do + pool.wait_for_completion + end + +feature {NONE} -- Access + + pool: THREAD_POOL [HTTPD_REQUEST_HANDLER] --ANY] --POOLED_THREAD [HTTP_REQUEST_HANDLER]] + -- Pool of concurrent connection handlers. + +invariant + pool_attached: pool /= Void + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/concurrency/thread/httpd_request_handler.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/thread/httpd_request_handler.e new file mode 100644 index 00000000..a119e283 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/thread/httpd_request_handler.e @@ -0,0 +1,45 @@ +note + description: "[ + Instance of HTTPD_REQUEST_HANDLER will process the incoming connection + and extract information on the request and the server + ]" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTPD_REQUEST_HANDLER + +inherit + HTTPD_REQUEST_HANDLER_I + redefine + release + end + +feature {HTTPD_CONNECTION_HANDLER_I} -- Basic operation + + release (a_socket: HTTPD_STREAM_SOCKET) + local + d: STRING + do + -- FIXME: for log purpose + d := a_socket.descriptor.out + debug ("dbglog") + dbglog (generator + ".release: ENTER {" + d + "}") + end + Precursor {HTTPD_REQUEST_HANDLER_I} (a_socket) + debug ("dbglog") + dbglog (generator + ".release: LEAVE {" + d + "}") + end + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/concurrency/thread/httpd_request_handler_factory.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/thread/httpd_request_handler_factory.e new file mode 100644 index 00000000..8bff0870 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/thread/httpd_request_handler_factory.e @@ -0,0 +1,16 @@ +note + description: "Summary description for {HTTPD_REQUEST_HANDLER_FACTORY}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTPD_REQUEST_HANDLER_FACTORY + +inherit + HTTPD_REQUEST_HANDLER_FACTORY_I + +note + copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/thread/pool/pooled_thread.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/thread/pool/pooled_thread.e new file mode 100644 index 00000000..b39e18be --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/thread/pool/pooled_thread.e @@ -0,0 +1,121 @@ +note + description: "{POOLED_THREAD} is used in combination with {THREAD_POOL} to allow for pooled threads." + legal: "See notice at end of class." + status: "Community Preview 1.0" + date: "$Date: 2009-09-01 19:15:37 -0300 (mar 01 de sep de 2009) $" + revision: "$Revision: 80577 $" + +class + POOLED_THREAD [G] + +inherit + THREAD + rename + make as thread_make + end + +create {THREAD_POOL} + make + +feature {NONE} -- Initialization + + make (a_thread_pool: THREAD_POOL [G]; a_semaphore: SEMAPHORE) + -- `a_thread_pool', the pool in which this thread is managed + -- `a_semaphore' is used for execution suspending + do + thread_make + thread_pool := a_thread_pool + semaphore := a_semaphore + end + +feature {NONE} -- Access + + thread_pool: THREAD_POOL [G] + -- Pool manager in which this thread is pooled + + target: detachable G + -- Target on which the `thread_procedure' should be applied + -- Depending on which launch is used, target is not used + + thread_procedure: detachable PROCEDURE [G, TUPLE] + -- Work that should be executed by the thread + + semaphore: SEMAPHORE + -- Semaphore share with all threads in a thread pool + -- to suspend execution until more work is available + +feature -- Access + + set_target (a_target: G) + -- Sets the target on which the work should be executed + do + target := a_target + end + +feature {NONE} -- Implementation + + execute + -- + local + done: BOOLEAN + do + from + semaphore.wait + thread_procedure := thread_pool.get_work (Current) + until + done + loop + if attached thread_procedure as l_work then + if attached target as t then + l_work.call ([t]) + else + l_work.call (Void) + end + end + if thread_pool.over then + done := True + else + thread_procedure := thread_pool.get_work (Current) + if thread_procedure = Void then + semaphore.wait + thread_procedure := thread_pool.get_work (Current) + end + end + end + thread_pool.thread_terminated (Current) + end + +note + copyright: "2011-2012, Javier Velilla and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + licensing_options: "http://www.eiffel.com/licensing" + copying: "[ + This file is part of Eiffel Software's Eiffel Development Environment. + + Eiffel Software's Eiffel Development Environment is free + software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published + by the Free Software Foundation, version 2 of the License + (available at the URL listed under "license" above). + + Eiffel Software's Eiffel Development Environment is + distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with Eiffel Software's Eiffel Development + Environment; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ]" + 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/connectors/httpd/src/httpd/concurrency/thread/pool/thread_pool.e b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/thread/pool/thread_pool.e new file mode 100644 index 00000000..445aadae --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/concurrency/thread/pool/thread_pool.e @@ -0,0 +1,228 @@ +note + description: "[ + A thread pool manager. Manages threads up to `capacity' and queue after that, + till threads get available again. + ]" + legal: "See notice at end of class." + status: "Community Preview 1.0" + date: "$Date: 2009-09-01 19:15:37 -0300 (mar 01 de sep de 2009) $" + revision: "$Revision: 80577 $" + +class + THREAD_POOL [G] + +inherit + EXECUTION_ENVIRONMENT + +create + make + +feature {NONE} -- Initialization + + make (n: like capacity) + -- Initialize current pool with capacity `n'. + require + n_positive: n > 0 + n_not_too_large: n < {INTEGER_32}.max_value.as_natural_32 + local + i: NATURAL + do + capacity := n + create work_queue.make (n.to_integer_32) + create work_queue_mutex.make + create over_mutex.make + create termination_mutex.make + create work_semaphore.make (capacity.as_integer_32) + from + i := 1 + until + i > capacity + loop + work_semaphore.wait + i := i + 1 + end + initialize_threads + terminated_count := capacity + is_over := False + ensure + capacity_set: capacity = n + work_queue_set: work_queue.is_empty + end + + initialize_threads + -- Launches all threads + local + i: NATURAL + thread: POOLED_THREAD [G] + do + from + i := 1 + until + i > capacity + loop + create thread.make (Current, work_semaphore) + thread.launch + i := i + 1 + end + end + +feature -- Access + + capacity: NATURAL + -- Maximal number of threads allowed (queuing otherwise) + + queue_count: NATURAL + -- Number of items in queue + do + work_queue_mutex.lock + Result := work_queue.count.as_natural_32 + work_queue_mutex.unlock + end + +feature -- Status report + + valid_action (a_action: PROCEDURE [G, TUPLE]): BOOLEAN + -- Is `a_action' a valid action for the current pool. + do + -- There should be no open operands. + Result := a_action.valid_operands (Void) + end + +feature -- Basic operations + + add_work (work: PROCEDURE [G, TUPLE]) + -- Launches a thread with the specified argument `arg'. Reuse of thread if possible. + require + valid_action: valid_action (work) + do + work_queue_mutex.lock + work_queue.extend (work) + if work_queue.count <= capacity.as_integer_32 then + -- Let one thread wake up and do the work + work_semaphore.post + end + work_queue_mutex.unlock + end + + over: BOOLEAN + -- Is the thread pool being terminated? + do + over_mutex.lock + Result := is_over + over_mutex.unlock + end + + thread_terminated (a_thread: POOLED_THREAD [G]) + -- Notifies the thread pool that a thread has terminated its execution. + do + termination_mutex.lock + terminated_count := terminated_count - 1 + termination_mutex.unlock + end + + get_work (requester: POOLED_THREAD [G]): detachable PROCEDURE [G, TUPLE] + -- If there is work to do, it is returned + -- Yields Void otherwise + do + if not over then + work_queue_mutex.lock + if not work_queue.is_empty then + Result := work_queue.item + work_queue.remove + end + work_queue_mutex.unlock + end + end + + wait_for_completion + -- Wait until there is no more work to be completed + local + done: BOOLEAN + do + from + + until + done + loop + work_queue_mutex.lock + done := work_queue.is_empty + work_queue_mutex.unlock + if not done then + sleep (1) + end + end + end + + terminate + -- Terminates all the threads after their execution + do + over_mutex.lock + is_over := True + over_mutex.unlock + from + termination_mutex.lock + until + terminated_count = 0 + loop + work_semaphore.post + termination_mutex.unlock + termination_mutex.lock + end + termination_mutex.unlock + end + +feature {NONE} -- Implementation: Access + + work_queue: ARRAYED_QUEUE [PROCEDURE [G, TUPLE]] + -- Queue that holds unprocessed requests as agents + -- Thread-safe access when accessor holds `queue_mutex' + + work_queue_mutex: MUTEX + -- Mutex for the queue + + work_semaphore: SEMAPHORE + -- Semaphore which hols the number of work to be done. + -- Needed to wake up worker threads + + terminated_count: NATURAL + -- + + is_over: BOOLEAN + -- Is the thread pool being terminated? + + over_mutex: MUTEX + -- Mutex for the `is_over' variable + + termination_mutex: MUTEX +;note + copyright: "2011-2012, Javier Velilla, Jocelyn Fiat and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + licensing_options: "http://www.eiffel.com/licensing" + copying: "[ + This file is part of Eiffel Software's Eiffel Development Environment. + + Eiffel Software's Eiffel Development Environment is free + software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published + by the Free Software Foundation, version 2 of the License + (available at the URL listed under "license" above). + + Eiffel Software's Eiffel Development Environment is + distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty + of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with Eiffel Software's Eiffel Development + Environment; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ]" + 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/connectors/httpd/src/httpd/configuration/httpd_configuration_i.e b/library/server/ewsgi/connectors/httpd/src/httpd/configuration/httpd_configuration_i.e new file mode 100644 index 00000000..7acf397b --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/configuration/httpd_configuration_i.e @@ -0,0 +1,209 @@ +note + description: "Summary description for {HTTPD_CONFIGURATION_I}." + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTPD_CONFIGURATION_I + +feature {NONE} -- Initialization + + make + do + http_server_port := 80 + max_concurrent_connections := 100 + max_tcp_clients := 100 + socket_accept_timeout := 1_000 + socket_connect_timeout := 5_000 + keep_alive_timeout := 5 + is_secure := False + create ca_crt.make_empty + create ca_key.make_empty + end + +feature -- Access + + Server_details: STRING_8 + deferred + end + + http_server_name: detachable READABLE_STRING_8 assign set_http_server_name + http_server_port: INTEGER assign set_http_server_port + max_tcp_clients: INTEGER assign set_max_tcp_clients + max_concurrent_connections: INTEGER assign set_max_concurrent_connections + socket_accept_timeout: INTEGER assign set_socket_accept_timeout + socket_connect_timeout: INTEGER assign set_socket_connect_timeout + force_single_threaded: BOOLEAN assign set_force_single_threaded + do + Result := (max_concurrent_connections = 0) + end + + is_verbose: BOOLEAN assign set_is_verbose + -- Display verbose message to the output? + + keep_alive_timeout: INTEGER assign set_keep_alive_timeout + -- Persistent connection timeout + -- Timeout unit in Seconds. + + has_ssl_support: BOOLEAN + -- Has SSL support? + deferred + end + +feature -- Access: SSL + + is_secure: BOOLEAN + -- Is SSL/TLS session?. + + ca_crt: STRING + + ca_key: STRING + + ssl_protocol: NATURAL + -- By default protocol is tls 1.2. + +feature -- Element change + + set_http_server_name (v: detachable separate READABLE_STRING_8) + do + if v = Void then + unset_http_server_name +-- http_server_name := Void + else + create {IMMUTABLE_STRING_8} http_server_name.make_from_separate (v) + end + end + + unset_http_server_name + do + http_server_name := Void + end + + set_http_server_port (v: like http_server_port) + do + http_server_port := v + end + + set_max_tcp_clients (v: like max_tcp_clients) + do + max_tcp_clients := v + end + + set_max_concurrent_connections (v: like max_concurrent_connections) + do + max_concurrent_connections := v + end + + set_socket_accept_timeout (v: like socket_accept_timeout) + do + socket_accept_timeout := v + end + + set_socket_connect_timeout (v: like socket_connect_timeout) + do + socket_connect_timeout := v + end + + set_force_single_threaded (v: like force_single_threaded) + do + if v then + set_max_concurrent_connections (0) + end + end + + set_is_verbose (b: BOOLEAN) + -- Set `is_verbose' to `b' + do + is_verbose := b + end + + set_keep_alive_timeout (a_seconds: like keep_alive_timeout) + -- Set `keep_alive_timeout' with `a_seconds' + do + keep_alive_timeout := a_seconds + ensure + keep_alive_timeout_set: keep_alive_timeout = a_seconds + end + + mark_secure + -- Set is_secure in True + do + if has_ssl_support then + is_secure := True + if http_server_port = 80 then + set_http_server_port (443) + end + else + is_secure := False + end + end + +feature -- Element change + + set_ca_crt (a_value: STRING) + -- Set `ca_crt' with `a_value' + do + ca_crt := a_value + ensure + ca_crt_set: ca_crt = a_value + end + + set_ca_key (a_value: STRING) + -- Set `ca_key' with `a_value' + do + ca_key := a_value + ensure + ca_key_set: ca_key = a_value + end + + set_ssl_protocol (a_version: NATURAL) + -- Set `ssl_protocol' with `a_version' + do + ssl_protocol := a_version + ensure + ssl_protocol_set: ssl_protocol = a_version + end + +feature -- SSL Helpers + + set_ssl_protocol_to_ssl_2_or_3 + -- Set `ssl_protocol' with `Ssl_23'. + deferred + end + + set_ssl_protocol_to_ssl_3 + -- Set `ssl_protocol' with `Ssl_3'. + deferred + end + + set_ssl_protocol_to_tls_1_0 + -- Set `ssl_protocol' with `Tls_1_0'. + deferred + end + + set_ssl_protocol_to_tls_1_1 + -- Set `ssl_protocol' with `Tls_1_1'. + deferred + end + + set_ssl_protocol_to_tls_1_2 + -- Set `ssl_protocol' with `Tls_1_2'. + deferred + end + + set_ssl_protocol_to_dtls_1_0 + -- Set `ssl_protocol' with `Dtls_1_0'. + deferred + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/httpd-safe.ecf b/library/server/ewsgi/connectors/httpd/src/httpd/httpd-safe.ecf new file mode 100644 index 00000000..d6691fca --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/httpd-safe.ecf @@ -0,0 +1,63 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + + + + + + + + + + + + + /EIFGENs$ + /concurrency$ + /ssl$ + /no_ssl$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/server/ewsgi/connectors/httpd/src/httpd/httpd.ecf b/library/server/ewsgi/connectors/httpd/src/httpd/httpd.ecf new file mode 100644 index 00000000..aa6a08a0 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/httpd.ecf @@ -0,0 +1,62 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + + + + + + + + + + + + + + /EIFGENs$ + /concurrency$ + /ssl$ + /no_ssl$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/server/ewsgi/connectors/httpd/src/httpd/httpd_connection_handler_i.e b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_connection_handler_i.e new file mode 100644 index 00000000..d9708723 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_connection_handler_i.e @@ -0,0 +1,84 @@ +note + description: "Summary description for {HTTPD_CONNECTION_HANDLER_I}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTPD_CONNECTION_HANDLER_I + +inherit + HTTPD_DEBUG_FACILITIES + +feature {NONE} -- Initialization + + frozen make (a_server: like server) + do + server := a_server + factory := separate_factory (a_server) + initialize + end + + initialize + deferred + end + + separate_factory (a_server: like server): like factory + -- Separate factory from `a_server'. + --| required by SCOOP design. + do + Result := a_server.factory + end + +feature {NONE} -- Access + + factory: separate HTTPD_REQUEST_HANDLER_FACTORY + + server: separate HTTPD_SERVER_I + +feature {HTTPD_SERVER_I} -- Execution + + process_incoming_connection (a_socket: HTTPD_STREAM_SOCKET) + deferred + end + + shutdown + deferred + end + + wait_for_completion + -- Wait until Current completed any pending task + deferred + end + +feature {HTTPD_SERVER} -- Status report + + is_shutdown_requested: BOOLEAN + deferred + end + +feature {NONE} -- Output + + log (a_message: separate READABLE_STRING_8) + -- Log `a_message' + do + -- FIXME: Concurrency issue on `server' + separate_server_log (server, a_message) + end + + separate_server_log (a_server: like server; a_message: separate READABLE_STRING_8) + do + a_server.log (a_message) + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/httpd_controller.e b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_controller.e new file mode 100644 index 00000000..8693a71d --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_controller.e @@ -0,0 +1,22 @@ +note + description: "Summary description for {HTTPD_CONTROLLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + HTTPD_CONTROLLER + +feature -- Operation + + shutdown + do + shutdown_requested := True + end + + shutdown_requested: BOOLEAN + +;note + copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" +end diff --git a/library/server/ewsgi/connectors/httpd/src/httpd/httpd_debug_facilities.e b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_debug_facilities.e new file mode 100644 index 00000000..ac1b0051 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_debug_facilities.e @@ -0,0 +1,40 @@ +note + description: "Summary description for {HTTPD_DEBUG_FACILITIES}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTPD_DEBUG_FACILITIES + +feature {NONE} -- Output + + dbglog (m: READABLE_STRING_8) + require + not m.ends_with_general ("%N") + do + debug ("dbglog") + print ("[EWF/DBG] <#" + processor_id_from_object (Current).out + "> " + m + "%N") + end + end + +feature -- runtime + + frozen processor_id_from_object (a_object: separate ANY): INTEGER_32 + external + "C inline use %"eif_scoop.h%"" + alias + "RTS_PID(eif_access($a_object))" + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/httpd_logger.e b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_logger.e new file mode 100644 index 00000000..c9668af9 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_logger.e @@ -0,0 +1,27 @@ +note + description: "Summary description for {HTTPD_LOGGER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTPD_LOGGER + +feature -- Logs + + log (a_message: separate READABLE_STRING_8) + -- Log `a_message' + deferred + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/httpd_request_handler_factory_i.e b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_request_handler_factory_i.e new file mode 100644 index 00000000..940d7a0a --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_request_handler_factory_i.e @@ -0,0 +1,26 @@ +note + description: "Summary description for {HTTPD_REQUEST_HANDLER_FACTORY_I}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTPD_REQUEST_HANDLER_FACTORY_I + +feature -- Factory + + new_handler: separate HTTPD_REQUEST_HANDLER + deferred + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/httpd_request_handler_i.e b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_request_handler_i.e new file mode 100644 index 00000000..5be6fca8 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_request_handler_i.e @@ -0,0 +1,278 @@ +note + description: "Summary description for {HTTPD_REQUEST_HANDLER_I}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTPD_REQUEST_HANDLER_I + +inherit + HTTPD_DEBUG_FACILITIES + +feature {NONE} -- Initialization + + make + do + reset + end + + reset + do + has_error := False + version := Void + remote_info := Void + +-- if attached client_socket as l_sock then +-- l_sock.cleanup +-- end +-- client_socket := Void + + -- FIXME: optimize to just wipe_out if needed + create method.make_empty + create uri.make_empty + create request_header.make_empty + create request_header_map.make (10) + end + +feature -- Access + +-- client_socket: detachable TCP_STREAM_SOCKET + + request_header: STRING + -- Header' source + + request_header_map: HASH_TABLE [STRING, STRING] + -- Contains key:value of the header + + method: STRING + -- http verb + + uri: STRING + -- http endpoint + + version: detachable STRING + -- http_version + --| unused for now + + remote_info: detachable TUPLE [addr: STRING; hostname: STRING; port: INTEGER] + -- Information related to remote client + +feature -- Settings + + is_verbose: BOOLEAN + +feature -- Status report + + has_error: BOOLEAN + -- Error occurred during `analyze_request_message' + +feature -- Change + +-- set_client_socket (a_socket: separate TCP_STREAM_SOCKET) +-- require +-- socket_attached: a_socket /= Void +-- socket_valid: a_socket.is_open_read and then a_socket.is_open_write +-- a_http_socket: not a_socket.is_closed +-- deferred +-- ensure +-- attached client_socket as s implies s.descriptor = a_socket.descriptor +-- end + + set_is_verbose (b: BOOLEAN) + do + is_verbose := b + end + +feature -- Execution + + execute (a_socket: HTTPD_STREAM_SOCKET) + require + socket_attached: a_socket /= Void + socket_valid: a_socket.is_open_read and then a_socket.is_open_write + a_http_socket: not a_socket.is_closed + local + l_remote_info: detachable like remote_info + l_continue: BOOLEAN + do + if a_socket.is_closed then + debug ("dbglog") + dbglog (generator + ".execute {socket is Closed!}") + end + else + debug ("dbglog") + dbglog (generator + ".ENTER execute {" + a_socket.descriptor.out + "}") + end + from until l_continue loop + if a_socket.ready_for_reading then + l_continue := True + create l_remote_info + if attached a_socket.peer_address as l_addr then + l_remote_info.addr := l_addr.host_address.host_address + l_remote_info.hostname := l_addr.host_address.host_name + l_remote_info.port := l_addr.port + remote_info := l_remote_info + end + analyze_request_message (a_socket) + else + log (generator + ".WAITING execute {" + a_socket.descriptor.out + "}") + end + end + + if has_error then +-- check catch_bad_incoming_connection: False end + if is_verbose then +-- check invalid_incoming_request: False end + log ("ERROR: invalid HTTP incoming request") + end + else + process_request (a_socket) + end + debug ("dbglog") + dbglog (generator + ".LEAVE execute {" + a_socket.descriptor.out + "}") + end + end +-- release (a_socket) + end + + release (a_socket: HTTPD_STREAM_SOCKET) + do + a_socket.cleanup + reset + end + +feature -- Request processing + + process_request (a_socket: HTTPD_STREAM_SOCKET) + -- Process request ... + require + no_error: not has_error + a_uri_attached: uri /= Void + a_method_attached: method /= Void + a_header_map_attached: request_header_map /= Void + a_header_text_attached: request_header /= Void + a_socket_attached: a_socket /= Void + deferred + end + +feature -- Parsing + + analyze_request_message (a_socket: HTTPD_STREAM_SOCKET) + -- Analyze message extracted from `a_socket' as HTTP request + require + input_readable: a_socket /= Void and then a_socket.is_open_read + local + end_of_stream: BOOLEAN + pos, n: INTEGER + line: detachable STRING + k, val: STRING + txt: STRING + l_is_verbose: BOOLEAN + do + create txt.make (64) + request_header := txt + if a_socket.is_readable and then attached next_line (a_socket) as l_request_line and then not l_request_line.is_empty then + txt.append (l_request_line) + txt.append_character ('%N') + analyze_request_line (l_request_line) + else + has_error := True + end + l_is_verbose := is_verbose + if not has_error or l_is_verbose then + -- if `is_verbose' we can try to print the request, even if it is a bad HTTP request + from + line := next_line (a_socket) + until + line = Void or end_of_stream + loop + n := line.count + if l_is_verbose then + log (line) + end + pos := line.index_of (':', 1) + if pos > 0 then + k := line.substring (1, pos - 1) + if line [pos + 1].is_space then + pos := pos + 1 + end + if line [n] = '%R' then + n := n - 1 + end + val := line.substring (pos + 1, n) + request_header_map.put (val, k) + end + txt.append (line) + txt.append_character ('%N') + if line.is_empty or else line [1] = '%R' then + end_of_stream := True + else + line := next_line (a_socket) + end + end + end + end + + analyze_request_line (line: STRING) + -- Analyze `line' as a HTTP request line + require + valid_line: line /= Void and then not line.is_empty + local + pos, next_pos: INTEGER + do + if is_verbose then + log ("%N## Parse HTTP request line ##") + log (line) + end + pos := line.index_of (' ', 1) + method := line.substring (1, pos - 1) + next_pos := line.index_of (' ', pos + 1) + uri := line.substring (pos + 1, next_pos - 1) + version := line.substring (next_pos + 1, line.count) + has_error := method.is_empty + end + + next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING + -- Next line fetched from `a_socket' is available. + require + is_readable: a_socket.is_open_read + do + if a_socket.socket_ok then + a_socket.read_line_thread_aware + Result := a_socket.last_string + end + end + +feature -- Output + + logger: detachable HTTPD_LOGGER + + set_logger (a_logger: like logger) + do + logger := a_logger + end + + log (m: STRING) + do + if attached logger as l_logger then + l_logger.log (m) + else + io.put_string (m + "%N") + end + end + +invariant + request_header_attached: request_header /= Void + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/httpd_server_i.e b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_server_i.e new file mode 100644 index 00000000..c0f92b47 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/httpd_server_i.e @@ -0,0 +1,316 @@ +note + description: "Summary description for {HTTPD_SERVER_I}." + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTPD_SERVER_I + +inherit + HTTPD_DEBUG_FACILITIES + + HTTPD_LOGGER + +feature {NONE} -- Initialization + + make (a_factory: like factory) + -- `a_cfg': server configuration + -- `a_factory': connection handler builder + do + make_configured (create {like configuration}.make, a_factory) + end + + make_configured (a_cfg: like configuration; a_factory: like factory) + -- `a_cfg': server configuration + -- `a_factory': connection handler builder + do + configuration := a_cfg + factory := a_factory + + build_controller + + initialize + end + + build_controller + -- Build `controller'. + do + create controller + end + + initialize + -- Initialize Current server. + do + is_shutdown_requested := False + end + +feature -- Access + + is_verbose: BOOLEAN + -- Is verbose for output messages. + + configuration: HTTPD_CONFIGURATION + -- Associated server configuration. + + controller: separate HTTPD_CONTROLLER + + factory: separate HTTPD_REQUEST_HANDLER_FACTORY + +feature -- Access: listening + + port: INTEGER + -- Effective listening port. + --| If 0 then it is not launched successfully! + +feature -- Status: listening + + is_launched: BOOLEAN + -- Server launched and listening on `port' + + is_terminated: BOOLEAN + -- Is terminated? + + is_shutdown_requested: BOOLEAN + -- Set true to stop accept loop + +feature {NONE} -- Access: server + + request_counter: INTEGER + -- request counter, incremented for each new incoming connection. + +feature -- Execution + + launch + do + apply_configuration + is_terminated := False + if is_verbose then + log ("%N%NStarting Web Application Server (port=" + configuration.http_server_port.out + "):%N") + end + is_shutdown_requested := False + listen + on_terminated + end + + on_terminated + require + is_terminated + do + if is_terminated then + log ("%N%NTerminating Web Application Server (port="+ port.out +"):%N") + end + if attached output as o then + o.flush + o.close + end + end + + shutdown_server + do + debug ("dbglog") + dbglog ("Shutdown requested") + end + is_shutdown_requested := True + controller_shutdown (controller) + end + + controller_shutdown (ctl: attached like controller) + do + ctl.shutdown + end + +feature -- Listening + + listen + -- + -- Creates a socket and connects to the http server. + -- `a_server': The main server object + local + l_listening_socket, + l_accepted_socket: detachable HTTPD_STREAM_SOCKET + l_http_port: INTEGER + l_connection_handler: HTTPD_CONNECTION_HANDLER + do + is_terminated := False + is_launched := False + port := 0 + is_shutdown_requested := False + l_http_port := configuration.http_server_port + + if + attached configuration.http_server_name as l_servername and then + attached (create {INET_ADDRESS_FACTORY}).create_from_name (l_servername) as l_addr + then + l_listening_socket := new_listening_socket (l_addr, l_http_port) + else + l_listening_socket := new_listening_socket (Void, l_http_port) + end + + if not l_listening_socket.is_bound then + if is_verbose then + log ("Socket could not be bound on port " + l_http_port.out) + end + else + l_http_port := l_listening_socket.port + create l_connection_handler.make (Current) + from + l_listening_socket.listen (configuration.max_tcp_clients) + if is_verbose and then configuration.is_secure then + log ("%NHTTP Connection Server ready on port " + l_http_port.out +" : https://localhost:" + l_http_port.out + "/") + elseif is_verbose then + log ("%NHTTP Connection Server ready on port " + l_http_port.out +" : http://localhost:" + l_http_port.out + "/") + end + on_launched (l_http_port) + until + is_shutdown_requested + loop + l_listening_socket.accept + if not is_shutdown_requested then + l_accepted_socket := l_listening_socket.accepted + if l_accepted_socket /= Void then + request_counter := request_counter + 1 + if is_verbose then + log ("#" + request_counter.out + "# Incoming connection...(socket:" + l_accepted_socket.descriptor.out + ")") + end + debug ("dbglog") + dbglog (generator + ".before process_incoming_connection {" + l_accepted_socket.descriptor.out + "}" ) + end + l_connection_handler.process_incoming_connection (l_accepted_socket) + debug ("dbglog") + dbglog (generator + ".after process_incoming_connection {" + l_accepted_socket.descriptor.out + "}") + end + end + end + update_is_shutdown_requested (l_connection_handler) + end + wait_for_connection_handler_completion (l_connection_handler) + l_listening_socket.cleanup + check + socket_is_closed: l_listening_socket.is_closed + end + end + if is_launched then + on_stopped + end + if is_verbose then + log ("HTTP Connection Server ends.") + end + rescue + log ("HTTP Connection Server shutdown due to exception. Please relaunch manually.") + + if l_listening_socket /= Void then + l_listening_socket.cleanup + check + listening_socket_is_closed: l_listening_socket.is_closed + end + end + if is_launched then + on_stopped + end + is_shutdown_requested := True + retry + end + +feature {NONE} -- Factory + + new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTPD_STREAM_SOCKET + do + if a_addr /= Void then + create Result.make_server_by_address_and_port (a_addr, a_http_port) + else + create Result.make_server_by_port (a_http_port) + end + end + +feature {NONE} -- Helpers + + wait_for_connection_handler_completion (h: HTTPD_CONNECTION_HANDLER) + do + h.wait_for_completion + debug ("dbglog") + dbglog ("Shutdown ready from connection_handler point of view") + end + end + + update_is_shutdown_requested (a_connection_handler: HTTPD_CONNECTION_HANDLER) + do + is_shutdown_requested := is_shutdown_requested or shutdown_requested (controller) + if is_shutdown_requested then + a_connection_handler.shutdown + end + end + + shutdown_requested (a_controller: separate HTTPD_CONTROLLER): BOOLEAN + -- Shutdown requested on concurrent `a_controller'? + do + Result := a_controller.shutdown_requested + end + +feature -- Event + + on_launched (a_port: INTEGER) + -- Server launched using port `a_port' + require + not_launched: not is_launched + do + is_launched := True + port := a_port + ensure + is_launched: is_launched + end + + on_stopped + -- Server stopped + require + is_launched: is_launched + do + is_launched := False + is_terminated := True + ensure + stopped: not is_launched + end + +feature -- Configuration change + + apply_configuration + require + is_not_launched: not is_launched + do + is_verbose := configuration.is_verbose + end + +feature -- Output + + output: detachable FILE + + set_log_output (f: FILE) + do + output := f + end + + log (a_message: separate READABLE_STRING_8) + -- Log `a_message' + local + m: STRING + do + create m.make_from_separate (a_message) + if attached output as o then + o.put_string (m) + o.put_new_line + else + io.error.put_string (m) + io.error.put_new_line + end + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/network/httpd_stream_socket.e b/library/server/ewsgi/connectors/httpd/src/httpd/network/httpd_stream_socket.e new file mode 100644 index 00000000..ff4a6361 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/network/httpd_stream_socket.e @@ -0,0 +1,252 @@ +note + description: "[ + Summary description for {HTTPD_STREAM_SOCKET} + that can be used for http or https connection. + ]" + date: "$Date$" + revision: "$Revision$" + +class + HTTPD_STREAM_SOCKET + +create + make_server_by_address_and_port, + make_server_by_port, + make_from_separate + +create {HTTPD_STREAM_SOCKET} + make + +feature {NONE} -- Initialization + + make_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER) + do + create {TCP_STREAM_SOCKET} socket.make_server_by_address_and_port (an_address, a_port) + end + + make_server_by_port (a_port: INTEGER) + do + create {TCP_STREAM_SOCKET} socket.make_server_by_port (a_port) + end + + make_from_separate (s: separate HTTPD_STREAM_SOCKET) + require + descriptor_available: s.descriptor_available + do + create {TCP_STREAM_SOCKET} socket.make_from_separate (s.socket) + end + + retrieve_socket (s: HTTPD_STREAM_SOCKET): INTEGER + do + Result := s.socket.descriptor + end + +feature -- Access + + last_string: STRING + do + Result := socket.last_string + end + + last_character: CHARACTER + do + Result := socket.last_character + end + + peer_address: detachable NETWORK_SOCKET_ADDRESS + -- Peer address of socket + do + if attached {NETWORK_SOCKET_ADDRESS} socket.peer_address as l_peer_address then + Result := l_peer_address + end + end + +feature -- Input + + read_line_thread_aware + do + socket.read_line_thread_aware + end + + read_stream_thread_aware (nb: INTEGER) + do + socket.read_stream_thread_aware (nb) + end + + read_stream (nb: INTEGER) + do + socket.read_stream (nb) + end + + read_character + do + socket.read_character + end + + bytes_read: INTEGER + do + Result := socket.bytes_read + end + +feature -- Output + + send_message (a_msg: STRING) + do + put_string (a_msg) + end + + put_readable_string_8 (s: READABLE_STRING_8) + -- Write readable string `s' to socket. + do + if attached {TCP_STREAM_SOCKET} socket as l_tcp_stream_socket then + l_tcp_stream_socket.put_readable_string_8 (s) + else + put_string (s) + end + end + + put_string (s: STRING) + do + socket.put_string (s) + end + + put_character (c: CHARACTER) + do + socket.put_character (c) + end + +feature -- Status Report + + descriptor_available: BOOLEAN + -- Is descriptor available? + do + Result := socket.descriptor_available + end + + descriptor: INTEGER + do + Result := socket.descriptor + end + + port: INTEGER + do + if attached {TCP_STREAM_SOCKET} socket as l_socket then + Result := l_socket.port + end + end + + is_blocking: BOOLEAN + do + Result := socket.is_blocking + end + + is_bound: BOOLEAN + do + if attached {TCP_STREAM_SOCKET} socket as l_socket then + Result := l_socket.is_bound + end + end + + socket_ok: BOOLEAN + do + Result := socket.socket_ok + end + + is_open_read: BOOLEAN + do + Result := socket.is_open_read + end + + is_open_write: BOOLEAN + do + Result := socket.is_open_write + end + + is_closed: BOOLEAN + do + Result := socket.is_closed + end + + is_readable: BOOLEAN + do + Result := socket.is_readable + end + + cleanup + do + socket.cleanup + end + + ready_for_writing: BOOLEAN + do + if attached {TCP_STREAM_SOCKET} socket as l_socket then + Result := l_socket.ready_for_writing + end + end + + listen (a_queue: INTEGER) + do + socket.listen (a_queue) + end + + accept + do + socket.accept + end + + set_blocking + do + socket.set_blocking + end + + set_non_blocking + do + socket.set_non_blocking + end + + readable: BOOLEAN + do + Result := socket.readable + end + + ready_for_reading: BOOLEAN + do + if attached {TCP_STREAM_SOCKET} socket as l_socket then + Result := l_socket.ready_for_reading + end + end + + try_ready_for_reading: BOOLEAN + do + if attached {TCP_STREAM_SOCKET} socket as l_socket then + Result := l_socket.try_ready_for_reading + end + end + + accepted: detachable HTTPD_STREAM_SOCKET + do + if attached socket.accepted as l_accepted then + create Result.make (l_accepted) + end + end + +feature {HTTPD_STREAM_SOCKET} -- Implementation + + make (a_socket: STREAM_SOCKET) + do + socket := a_socket + end + + socket: STREAM_SOCKET + +;note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/network/tcp_stream_socket.e b/library/server/ewsgi/connectors/httpd/src/httpd/network/tcp_stream_socket.e new file mode 100644 index 00000000..1713779d --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/network/tcp_stream_socket.e @@ -0,0 +1,92 @@ +note + description: "Summary description for {TCP_STREAM_SOCKET}." + date: "$Date$" + revision: "$Revision$" + +class + TCP_STREAM_SOCKET + +inherit + NETWORK_STREAM_SOCKET + redefine + make + end + +create + make_server_by_address_and_port, + make_server_by_port, + make_from_separate + +create {NETWORK_STREAM_SOCKET} + make_from_descriptor_and_address, + make_empty + +feature {NONE} -- Initialization + + make + -- Create a network stream socket. + do + Precursor + set_reuse_address + end + + make_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER) + -- Create server socket on `an_address' and `a_port'. + require + valid_port: a_port >= 0 + do + make + create address.make_from_address_and_port (an_address, a_port) + bind + end + + make_from_separate (s: separate STREAM_SOCKET) + require + descriptor_available: s.descriptor_available + do + create_from_descriptor (s.descriptor) + end + +feature -- Basic operation + + send_message (a_msg: STRING) + do + put_string (a_msg) + end + +feature -- Output + + put_readable_string_8 (s: READABLE_STRING_8) + -- Write readable string `s' to socket. + local + ext: C_STRING + do + create ext.make (s) + put_managed_pointer (ext.managed_data, 0, s.count) + end + +feature -- Status report + + try_ready_for_reading: BOOLEAN + -- Is data available for reading from the socket right now? + require + socket_exists: exists + local + retval: INTEGER + do + retval := c_select_poll_with_timeout (descriptor, True, 0) + Result := (retval > 0) + end + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/no_ssl/httpd_configuration.e b/library/server/ewsgi/connectors/httpd/src/httpd/no_ssl/httpd_configuration.e new file mode 100644 index 00000000..b62114ff --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/no_ssl/httpd_configuration.e @@ -0,0 +1,71 @@ +note + description: "Summary description for {HTTPD_CONFIGURATION}." + date: "$Date$" + revision: "$Revision$" + +class + HTTPD_CONFIGURATION + +inherit + HTTPD_CONFIGURATION_I + +create + make + +feature -- Status + + Server_details: STRING_8 = "Server : NINO Eiffel Server" + + has_ssl_support: BOOLEAN = False + -- Precursor + +feature -- SSL Helpers + + set_ssl_protocol_to_ssl_2_or_3 + -- Set `ssl_protocol' with `Ssl_23'. + do + -- Ignored + end + + set_ssl_protocol_to_ssl_3 + -- Set `ssl_protocol' with `Ssl_3'. + do + -- Ignored + end + + set_ssl_protocol_to_tls_1_0 + -- Set `ssl_protocol' with `Tls_1_0'. + do + -- Ignored + end + + set_ssl_protocol_to_tls_1_1 + -- Set `ssl_protocol' with `Tls_1_1'. + do + -- Ignored + end + + set_ssl_protocol_to_tls_1_2 + -- Set `ssl_protocol' with `Tls_1_2'. + do + -- Ignored + end + + set_ssl_protocol_to_dtls_1_0 + -- Set `ssl_protocol' with `Dtls_1_0'. + do + -- Ignored + end + + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/no_ssl/httpd_server.e b/library/server/ewsgi/connectors/httpd/src/httpd/no_ssl/httpd_server.e new file mode 100644 index 00000000..8787e1fc --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/no_ssl/httpd_server.e @@ -0,0 +1,27 @@ +note + description: "[ + httpd server + ]" + date: "$Date$" + revision: "$Revision$" + +class + HTTPD_SERVER + +inherit + HTTPD_SERVER_I + +create + make + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/ssl/httpd_configuration.e b/library/server/ewsgi/connectors/httpd/src/httpd/ssl/httpd_configuration.e new file mode 100644 index 00000000..275fd5e4 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/ssl/httpd_configuration.e @@ -0,0 +1,82 @@ +note + description: "Summary description for {HTTPD_CONFIGURATION}." + date: "$Date$" + revision: "$Revision$" + +class + HTTPD_CONFIGURATION + +inherit + HTTPD_CONFIGURATION_I + redefine + make + end + +create + make + +feature {NONE} -- Initialization + + make + do + Precursor + ssl_protocol := {SSL_PROTOCOL}.tls_1_2 + end + +feature -- Access + + Server_details: STRING_8 = "Server : NINO Eiffel Server (https)" + + has_ssl_support: BOOLEAN = True + -- Precursor + +feature -- SSL Helpers + + set_ssl_protocol_to_ssl_2_or_3 + -- Set `ssl_protocol' with `Ssl_23'. + do + set_ssl_protocol ({SSL_PROTOCOL}.Ssl_23) + end + + set_ssl_protocol_to_ssl_3 + -- Set `ssl_protocol' with `Ssl_3'. + do + set_ssl_protocol ({SSL_PROTOCOL}.Ssl_3) + end + + set_ssl_protocol_to_tls_1_0 + -- Set `ssl_protocol' with `Tls_1_0'. + do + set_ssl_protocol ({SSL_PROTOCOL}.Tls_1_0) + end + + set_ssl_protocol_to_tls_1_1 + -- Set `ssl_protocol' with `Tls_1_1'. + do + set_ssl_protocol ({SSL_PROTOCOL}.Tls_1_1) + end + + set_ssl_protocol_to_tls_1_2 + -- Set `ssl_protocol' with `Tls_1_2'. + do + set_ssl_protocol ({SSL_PROTOCOL}.Tls_1_2) + end + + set_ssl_protocol_to_dtls_1_0 + -- Set `ssl_protocol' with `Dtls_1_0'. + do + set_ssl_protocol ({SSL_PROTOCOL}.Dtls_1_0) + end + + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/ssl/httpd_server.e b/library/server/ewsgi/connectors/httpd/src/httpd/ssl/httpd_server.e new file mode 100644 index 00000000..4045aa29 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/ssl/httpd_server.e @@ -0,0 +1,35 @@ +note + description: "[ + SSL enabled server + ]" + date: "$Date$" + revision: "$Revision$" + +class + HTTPD_SERVER + +inherit + HTTPD_SERVER_I + redefine + new_listening_socket + end + +create + make + +feature {NONE} -- Factory + + new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTPD_STREAM_SOCKET + do + if configuration.is_secure then + if a_addr /= Void then + create {HTTPD_STREAM_SSL_SOCKET} Result.make_ssl_server_by_address_and_port (a_addr, a_http_port, configuration.ssl_protocol, configuration.ca_crt, configuration.ca_key) + else + create {HTTPD_STREAM_SSL_SOCKET} Result.make_ssl_server_by_port (a_http_port, configuration.ssl_protocol, configuration.ca_crt, configuration.ca_key) + end + else + Result := Precursor (a_addr, a_http_port) + end + end + +end diff --git a/library/server/ewsgi/connectors/httpd/src/httpd/ssl/httpd_stream_ssl_socket.e b/library/server/ewsgi/connectors/httpd/src/httpd/ssl/httpd_stream_ssl_socket.e new file mode 100644 index 00000000..e1f25ca0 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/ssl/httpd_stream_ssl_socket.e @@ -0,0 +1,139 @@ +note + description: "[ + Summary description for {HTTPD_STREAM_SSL_SOCKET} + that can be used for http or https connection. + ]" + date: "$Date$" + revision: "$Revision$" + +class + HTTPD_STREAM_SSL_SOCKET + +inherit + HTTPD_STREAM_SOCKET + redefine + port, + is_bound, + ready_for_writing, + ready_for_reading, + try_ready_for_reading, + put_readable_string_8 + end + +create + make_ssl_server_by_address_and_port, make_ssl_server_by_port, + make_server_by_address_and_port, make_server_by_port + +create {HTTPD_STREAM_SOCKET} + make + +feature {NONE} -- Initialization + + make_ssl_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt: STRING; a_key: STRING) + local + l_socket: SSL_TCP_STREAM_SOCKET + do + create l_socket.make_server_by_address_and_port (an_address, a_port) + l_socket.set_tls_protocol (a_ssl_protocol) + socket := l_socket + set_certificates (a_crt, a_key) + end + + make_ssl_server_by_port (a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt: STRING; a_key: STRING) + local + l_socket: SSL_TCP_STREAM_SOCKET + do + create l_socket.make_server_by_port (a_port) + l_socket.set_tls_protocol (a_ssl_protocol) + socket := l_socket + set_certificates (a_crt, a_key) + end + +feature -- Output + + put_readable_string_8 (s: READABLE_STRING_8) + -- + do + if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then + l_ssl_socket.put_readable_string_8 (s) + else + Precursor (s) + end + end + +feature -- Status Report + + port: INTEGER + -- + do + if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then + Result := l_ssl_socket.port + else + Result := Precursor + end + end + + is_bound: BOOLEAN + -- + do + if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then + Result := l_ssl_socket.is_bound + else + Result := Precursor + end + end + + ready_for_writing: BOOLEAN + -- + do + if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then + Result := l_ssl_socket.ready_for_writing + else + Result := Precursor + end + end + + ready_for_reading: BOOLEAN + -- + do + if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then + Result := l_ssl_socket.ready_for_reading + else + Result := Precursor + end + end + + try_ready_for_reading: BOOLEAN + do + if attached {SSL_TCP_STREAM_SOCKET} socket as l_socket then + Result := l_socket.try_ready_for_reading + else + Result := Precursor + end + end + +feature {HTTPD_STREAM_SOCKET} -- Implementation + + set_certificates (a_crt: STRING; a_key: STRING) + local + a_file_name: FILE_NAME + do + if attached {SSL_NETWORK_STREAM_SOCKET} socket as l_socket then + create a_file_name.make_from_string (a_crt) + l_socket.set_certificate_file_name (a_file_name) + create a_file_name.make_from_string (a_key) + l_socket.set_key_file_name (a_file_name) + end + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/httpd/ssl/ssl_tcp_stream_socket.e b/library/server/ewsgi/connectors/httpd/src/httpd/ssl/ssl_tcp_stream_socket.e new file mode 100644 index 00000000..c4f5e668 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/httpd/ssl/ssl_tcp_stream_socket.e @@ -0,0 +1,72 @@ +note + description: "Summary description for {SSL_TCP_STREAM_SOCKET}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + SSL_TCP_STREAM_SOCKET + +inherit + + SSL_NETWORK_STREAM_SOCKET + +create + make_server_by_address_and_port, make_server_by_port + +create {SSL_NETWORK_STREAM_SOCKET} + make_from_descriptor_and_address + +feature {NONE} -- Initialization + + make_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER) + -- Create server socket on `an_address' and `a_port'. + require + valid_port: a_port >= 0 + do + make + create address.make_from_address_and_port (an_address, a_port) + bind + end + +feature -- Basic operation + + send_message (a_msg: STRING) + do + put_string (a_msg) + end + +feature -- Output + + put_readable_string_8 (s: READABLE_STRING_8) + -- Write readable string `s' to socket. + local + ext: C_STRING + do + create ext.make (s) + put_managed_pointer (ext.managed_data, 0, s.count) + end + +feature -- Status report + + try_ready_for_reading: BOOLEAN + -- Is data available for reading from the socket right now? + require + socket_exists: exists + local + retval: INTEGER + do + retval := c_select_poll_with_timeout (descriptor, True, 0) + Result := (retval > 0) + end + +feature {NONE}-- Implementation + + + + +note + copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + +end diff --git a/library/server/ewsgi/connectors/httpd/src/wgi_httpd_error_stream.e b/library/server/ewsgi/connectors/httpd/src/wgi_httpd_error_stream.e new file mode 100644 index 00000000..3cb84e31 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/wgi_httpd_error_stream.e @@ -0,0 +1,70 @@ +note + description: "Summary description for WGI_HTTPD_ERROR_STREAM." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + WGI_HTTPD_ERROR_STREAM + +inherit + WGI_ERROR_STREAM + +create + make, + make_stderr, + make_stdout + +feature {NONE} -- Initialization + + make (a_identifier: READABLE_STRING_8; a_file: PLAIN_TEXT_FILE) + do + identifier := a_identifier + output := a_file + end + + make_stderr (a_identifier: READABLE_STRING_8) + do + make (a_identifier, io.error) + end + + make_stdout (a_identifier: READABLE_STRING_8) + do + make (a_identifier, io.error) + end + +feature -- Access + + identifier: READABLE_STRING_8 + + output: FILE + +feature -- Error + + put_error (a_message: READABLE_STRING_8) + local + s: STRING + do + create s.make (a_message.count + identifier.count + 4) + s.append_character ('[') + s.append (identifier) + s.append_character (']') + s.append_character (' ') + s.append (a_message) + s.append_character ('%N') + -- Display it at once. + output.put_string (s) + 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/connectors/httpd/src/wgi_httpd_input_stream.e b/library/server/ewsgi/connectors/httpd/src/wgi_httpd_input_stream.e new file mode 100644 index 00000000..878db547 --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/wgi_httpd_input_stream.e @@ -0,0 +1,100 @@ +note + description: "Summary description for {WGI_HTTPD_INPUT_STREAM}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + WGI_HTTPD_INPUT_STREAM + +inherit + WGI_INPUT_STREAM + +create + make + +feature {NONE} -- Initialization + + make (a_source: like source) + do + create last_string.make_empty + set_source (a_source) + end + +feature {WGI_NINO_CONNECTOR, WGI_SERVICE} -- Nino + + set_source (i: like source) + do + source := i + end + + source: HTTPD_STREAM_SOCKET + +feature -- Input + + read_character + -- Read the next character in input stream. + -- Make the result available in `last_character'. + local + src: like source + do + src := source + if src.readable then + src.read_character + last_character := src.last_character + else + last_character := '%U' + end + end + + read_string (nb: INTEGER) + local + src: like source + do + src := source + last_string.wipe_out + if src.readable then + src.read_stream_thread_aware (nb) + last_string.append_string (src.last_string) + end + end + +feature -- Access + + last_string: STRING_8 + -- Last string read + -- (Note: this query *might* return the same object. + -- Therefore a clone should be used if the result + -- is to be kept beyond the next call to this feature. + -- However `last_string' is not shared between input objects.) + + last_character: CHARACTER_8 + -- Last item read + +feature -- Status report + + is_open_read: BOOLEAN + -- Can items be read from input stream? + do + Result := source.is_open_read + end + + end_of_input: BOOLEAN + -- Has the end of input stream been reached? + do + Result := not source.try_ready_for_reading + end + +;note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/wgi_httpd_output_stream.e b/library/server/ewsgi/connectors/httpd/src/wgi_httpd_output_stream.e new file mode 100644 index 00000000..a57095cf --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/wgi_httpd_output_stream.e @@ -0,0 +1,104 @@ +note + description: "Summary description for {WGI_HTTPD_OUTPUT_STREAM}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + WGI_HTTPD_OUTPUT_STREAM + +inherit + WGI_OUTPUT_STREAM + + HTTP_STATUS_CODE_MESSAGES + export + {NONE} all + end + +create + make + +feature {NONE} -- Initialization + + make (a_target: like target) + do + set_target (a_target) + end + +feature {WGI_NINO_CONNECTOR, WGI_SERVICE} -- Nino + + set_target (o: like target) + do + target := o + end + + target: HTTPD_STREAM_SOCKET + +feature -- Status writing + + put_status_line (a_code: INTEGER; a_reason_phrase: detachable READABLE_STRING_8) + -- + local + s: STRING + m: detachable READABLE_STRING_8 + do + create s.make (16) + s.append ({HTTP_CONSTANTS}.http_version_1_1) + s.append_character (' ') + s.append_integer (a_code) + m := a_reason_phrase + if m = Void then + m := http_status_code_message (a_code) + end + if m /= Void then + s.append_character (' ') + s.append_string (m) + end + put_header_line (s) + end + +feature -- Output + + put_readable_string_8 (s: READABLE_STRING_8) + -- Send `s' to http client + do + target.put_readable_string_8 (s) + end + + put_string (s: READABLE_STRING_8) + -- Send `s' to http client + do + target.put_readable_string_8 (s) + end + + put_character (c: CHARACTER_8) + do + target.put_character (c) + end + +feature -- Status report + + is_open_write: BOOLEAN + -- Can items be written to output stream? + do + Result := target.is_open_write + end + +feature -- Basic operations + + flush + do + end + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, 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/connectors/httpd/src/wgi_httpd_response_stream.e b/library/server/ewsgi/connectors/httpd/src/wgi_httpd_response_stream.e new file mode 100644 index 00000000..acc8a3cd --- /dev/null +++ b/library/server/ewsgi/connectors/httpd/src/wgi_httpd_response_stream.e @@ -0,0 +1,50 @@ +note + description: "[ + WGI Response implemented using stream buffer + + ]" + date: "$Date$" + revision: "$Revision$" + +class + WGI_HTTPD_RESPONSE_STREAM + +inherit + WGI_RESPONSE_STREAM + redefine + put_header_text + end + +create + make + +feature -- Header output operation + + put_header_text (a_text: READABLE_STRING_8) + local + o: like output + do + o := output + o.put_string (a_text) + + -- Nino does not support persistent connection for now + o.put_string ("Connection: close") + o.put_crlf + + -- end of headers + o.put_crlf + + header_committed := True + end + +;note + copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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/request/wgi_request.e b/library/server/ewsgi/specification/request/wgi_request.e index 5054650b..85a9660e 100644 --- a/library/server/ewsgi/specification/request/wgi_request.e +++ b/library/server/ewsgi/specification/request/wgi_request.e @@ -157,7 +157,7 @@ feature -- Eiffel WGI access deferred end - wgi_connector: WGI_CONNECTOR + wgi_connector: detachable WGI_CONNECTOR -- Associated Eiffel WGI connector deferred end @@ -685,7 +685,7 @@ invariant path_info_identical: path_info ~ meta_string_variable ({WGI_META_NAMES}.path_info) note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/server/ewsgi/specification/response/wgi_response.e b/library/server/ewsgi/specification/response/wgi_response.e index 8d8f9de5..d0192a5a 100644 --- a/library/server/ewsgi/specification/response/wgi_response.e +++ b/library/server/ewsgi/specification/response/wgi_response.e @@ -6,7 +6,7 @@ note deferred class WGI_RESPONSE -feature {WGI_CONNECTOR, WGI_SERVICE} -- Commit +feature {WGI_CONNECTOR, WGI_SERVICE, WGI_EXPORTER} -- Commit push -- Commit and push response @@ -156,7 +156,7 @@ feature -- Error reporting end note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/server/ewsgi/src/implementation/wgi_request_from_table.e b/library/server/ewsgi/src/implementation/wgi_request_from_table.e index 57d7513f..988d0c99 100644 --- a/library/server/ewsgi/src/implementation/wgi_request_from_table.e +++ b/library/server/ewsgi/src/implementation/wgi_request_from_table.e @@ -57,7 +57,7 @@ feature -- EWSGI access wgi_implementation: STRING = "Eiffel Web Framework 0.1" - wgi_connector: WGI_CONNECTOR + wgi_connector: detachable WGI_CONNECTOR feature -- Access: CGI meta parameters @@ -289,7 +289,7 @@ feature -- Access: HTTP_* CGI meta parameters - 1.1 do Result := meta_string_variable ({WGI_META_NAMES}.http_range) end - + http_content_range: detachable READABLE_STRING_8 -- Partial range of selected representation enclosed in message payload do @@ -301,8 +301,8 @@ feature -- Access: HTTP_* CGI meta parameters - 1.1 do Result := meta_string_variable ({WGI_META_NAMES}.http_content_encoding) end - - + + feature -- Access: Extension to CGI meta parameters - 1.1 request_uri: READABLE_STRING_8 @@ -507,7 +507,7 @@ invariant empty_string_unchanged: empty_string.is_empty note - copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/server/ewsgi/src/wgi_exporter.e b/library/server/ewsgi/src/wgi_exporter.e new file mode 100644 index 00000000..7fd4e349 --- /dev/null +++ b/library/server/ewsgi/src/wgi_exporter.e @@ -0,0 +1,10 @@ +note + description: "Summary description for {WGI_EXPORTER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + WGI_EXPORTER + +end diff --git a/library/server/wsf/src/wsf_execution.e b/library/server/wsf/src/wsf_execution.e new file mode 100644 index 00000000..582fb6d2 --- /dev/null +++ b/library/server/wsf/src/wsf_execution.e @@ -0,0 +1,43 @@ +note + description: "Summary description for {WSF_EXECUTION}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + WSF_EXECUTION + +--create +-- make + +feature {NONE} -- Initialization + + make (req: WGI_REQUEST; res: WGI_RESPONSE) + do + create request.make_from_wgi (req) + create response.make_from_wgi (res) + end + + request: WSF_REQUEST + + response: WSF_RESPONSE + +feature -- Execution + + execute + deferred + ensure + response.status_is_set + end + +note + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, 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/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index b36f8fd6..5f17f910 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -41,7 +41,7 @@ inherit {NONE} all end -create {WSF_TO_WGI_SERVICE} +create {WSF_TO_WGI_SERVICE, WSF_EXECUTION} make_from_wgi convert @@ -426,7 +426,7 @@ feature -- Eiffel WGI access Result := wgi_request.wgi_implementation end - wgi_connector: WGI_CONNECTOR + wgi_connector: detachable WGI_CONNECTOR -- Associated Eiffel WGI connector do Result := wgi_request.wgi_connector @@ -2173,7 +2173,7 @@ invariant wgi_request.content_type /= Void implies content_type /= Void note - copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" + copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/server/wsf/src/wsf_response.e b/library/server/wsf/src/wsf_response.e index f983f9d3..a158fda3 100644 --- a/library/server/wsf/src/wsf_response.e +++ b/library/server/wsf/src/wsf_response.e @@ -19,7 +19,7 @@ note class WSF_RESPONSE -create {WSF_TO_WGI_SERVICE} +create {WSF_TO_WGI_SERVICE, WSF_EXECUTION} make_from_wgi create {WSF_RESPONSE}