From aed7461faf222b15ec3fecd4e703f795317c26d0 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Sat, 8 Oct 2016 01:10:16 +0200 Subject: [PATCH] Improved networking implementation for httpd server and sockets. Use new EiffelNet routines that do not raise exception on error. Made compilable with 16.05 and dev-and-upcoming release 16.11. Fixed various minor issues related to base_url, and added comments. --- .../lib/httpd/http_network-safe.ecf | 33 +- .../standalone/lib/httpd/http_network.ecf | 32 +- .../standalone/lib/httpd/httpd-safe.ecf | 60 ++- .../connectors/standalone/lib/httpd/httpd.ecf | 34 +- .../lib/httpd/httpd_request_handler_i.e | 214 ++++---- .../from_16_11/tcp_stream_socket_ext.e | 9 - .../lib/httpd/network/httpd_stream_socket.e | 497 +++++++----------- .../httpd/network/httpd_stream_socket_ext.e | 13 + .../network/ssl/httpd_stream_ssl_socket.e | 242 ++++----- .../network/ssl/httpd_stream_ssl_socket_ext.e | 23 + .../httpd/network/ssl/ssl_tcp_stream_socket.e | 73 --- .../lib/httpd/network/tcp_stream_socket.e | 153 ------ .../until_16_05/httpd_stream_socket_ext.e | 160 ++++++ .../ssl/httpd_stream_ssl_socket_ext.e | 41 ++ .../until_16_05/tcp_stream_socket_ext.e | 95 ---- .../spec/include_until_16_05/ew_httpd_net.h | 52 ++ .../standalone/lib/httpd/ssl/httpd_server.e | 10 +- .../standalone/src/wgi_standalone_connector.e | 36 +- .../src/wgi_standalone_output_stream.e | 12 +- .../wsf_standalone_service_launcher.e | 11 +- 20 files changed, 858 insertions(+), 942 deletions(-) delete mode 100644 library/server/ewsgi/connectors/standalone/lib/httpd/network/from_16_11/tcp_stream_socket_ext.e create mode 100644 library/server/ewsgi/connectors/standalone/lib/httpd/network/httpd_stream_socket_ext.e create mode 100644 library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/httpd_stream_ssl_socket_ext.e delete mode 100644 library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/ssl_tcp_stream_socket.e delete mode 100644 library/server/ewsgi/connectors/standalone/lib/httpd/network/tcp_stream_socket.e create mode 100644 library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/httpd_stream_socket_ext.e create mode 100644 library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/ssl/httpd_stream_ssl_socket_ext.e delete mode 100644 library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/tcp_stream_socket_ext.e create mode 100644 library/server/ewsgi/connectors/standalone/lib/httpd/spec/include_until_16_05/ew_httpd_net.h diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/http_network-safe.ecf b/library/server/ewsgi/connectors/standalone/lib/httpd/http_network-safe.ecf index faea6dd5..68edf7df 100644 --- a/library/server/ewsgi/connectors/standalone/lib/httpd/http_network-safe.ecf +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/http_network-safe.ecf @@ -18,21 +18,36 @@ - - - - - - + + /httpd_stream_socket_ext.e$ - - + + - + + + + /httpd_stream_ssl_socket_ext.e$ + + + + + + + + + + + + + + + + diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/http_network.ecf b/library/server/ewsgi/connectors/standalone/lib/httpd/http_network.ecf index 25cc31ea..9f4067ec 100644 --- a/library/server/ewsgi/connectors/standalone/lib/httpd/http_network.ecf +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/http_network.ecf @@ -19,19 +19,33 @@ - - - - - - + + /httpd_stream_socket_ext.e$ - - + + - + + + + /httpd_stream_ssl_socket_ext.e$ + + + + + + + + + + + + + + + diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd-safe.ecf b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd-safe.ecf index d25947d3..7f1b6694 100644 --- a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd-safe.ecf +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd-safe.ecf @@ -11,6 +11,16 @@ + + + + + + + + + + @@ -24,29 +34,12 @@ - - - - - - - - - - - - - - - - - /concurrency$ + /network$ /no_ssl$ /ssl$ - /network$ @@ -74,5 +67,36 @@ + + + /httpd_stream_socket_ext.e$ + + + + + + + + + + /httpd_stream_ssl_socket_ext.e$ + + + + + + + + + + + + + + + + + + diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd.ecf b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd.ecf index 9d62c604..e6e87f66 100644 --- a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd.ecf +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd.ecf @@ -23,20 +23,34 @@ - - - - - - - + + + /httpd_stream_socket_ext.e$ - - + + - + + + + /httpd_stream_ssl_socket_ext.e$ + + + + + + + + + + + + + + + diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_request_handler_i.e b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_request_handler_i.e index 5823c07f..42e99fcb 100644 --- a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_request_handler_i.e +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_request_handler_i.e @@ -154,7 +154,7 @@ feature -- Settings feature -- Status report has_error: BOOLEAN - -- Error occurred during `analyze_request_message' + -- Error occurred during `get_request_header' feature -- Status change @@ -208,11 +208,21 @@ feature -- Execution is_connected: is_connected local l_socket: like client_socket + l_remote_info: detachable like remote_info l_exit: BOOLEAN n,m: INTEGER do l_socket := client_socket - l_socket.set_recv_timeout (socket_recv_timeout) + + -- Compute remote info once for the persistent connection. + create l_remote_info + if attached l_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 + end + remote_info := l_remote_info + check socket_attached: l_socket /= Void socket_valid: l_socket.is_open_read and then l_socket.is_open_write @@ -252,9 +262,7 @@ feature -- Execution reuse_connection_when_possible: a_is_reusing_connection implies is_persistent_connection_supported no_error: not has_error local - l_remote_info: detachable like remote_info l_socket: like client_socket - l_is_ready: BOOLEAN do debug ("dbglog") if a_is_reusing_connection then @@ -276,50 +284,34 @@ feature -- Execution dbglog ("execute_request socket=" + l_socket.descriptor.out + " ENTER") end - if a_is_reusing_connection then - --| set by default 5 seconds. - l_socket.set_recv_timeout (keep_alive_timeout) -- in seconds! - l_is_ready := socket_has_incoming_data (l_socket) - else - l_is_ready := True - end + -- Try to get request header. + -- If the request is reusing persistent connection, use `keep_alive_timeout', + -- otherwise `socket_recv_timeout'. + get_request_header (l_socket, a_is_reusing_connection) - if l_is_ready then - l_socket.set_recv_timeout (socket_recv_timeout) -- FIXME: return a 408 Request Timeout response .. - create l_remote_info - if attached l_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 (l_socket) - - if has_error then - -- check catch_bad_incoming_connection: False end + if has_error then + if a_is_reusing_connection and then request_header.is_empty then + -- Close persistent connection, since no new connection occurred in the delay `keep_alive_timeout'. + debug ("dbglog") + dbglog ("execute_request socket=" + l_socket.descriptor.out + "} close persistent connection.") + end + else if is_verbose then log (request_header + "%NWARNING: invalid HTTP incoming request", warning_level) end process_bad_request (l_socket) - is_persistent_connection_requested := False - else - if is_verbose then - log (request_header, information_level) - end - process_request (l_socket) - end - else - check is_reusing_connection: a_is_reusing_connection end - -- Close persistent connection, since no new connection occurred in the delay `keep_alive_timeout'. - is_persistent_connection_requested := False - debug ("dbglog") - dbglog ("execute_request socket=" + l_socket.descriptor.out + "} close persistent connection.") - end + end + is_persistent_connection_requested := False + else + if is_verbose then + log (request_header, information_level) + end + process_request (l_socket) end - debug ("dbglog") - dbglog ("execute_request {" + l_socket.descriptor.out + "} LEAVE") - end + debug ("dbglog") + dbglog ("execute_request {" + l_socket.descriptor.out + "} LEAVE") + end end end @@ -377,8 +369,11 @@ feature -- Request processing feature -- Parsing - analyze_request_message (a_socket: HTTPD_STREAM_SOCKET) - -- Analyze message extracted from `a_socket' as HTTP request + get_request_header (a_socket: HTTPD_STREAM_SOCKET; a_is_reusing_connection: BOOLEAN) + -- Analyze message extracted from `a_socket' as HTTP request. + -- If `a_is_reusing_connection' is True, then first use + -- Note: it reads from socket. + -- Note: it updates `request_header' and `request_header_map', and eventually `is_persistent_connection_requested'. require input_readable: a_socket /= Void and then a_socket.is_open_read local @@ -391,76 +386,90 @@ feature -- Parsing do create txt.make (64) request_header := txt + l_is_verbose := is_verbose + if not has_error and then a_socket.readable then + if a_is_reusing_connection then + a_socket.set_recv_timeout (keep_alive_timeout) -- in seconds! + else + a_socket.set_recv_timeout (socket_recv_timeout) -- FIXME: return a 408 Request Timeout response .. + end + if attached next_line (a_socket) as l_request_line and then - not l_request_line.is_empty + not l_request_line.is_empty and then + not has_error then txt.append (l_request_line) txt.append_character ('%N') analyze_request_line (l_request_line) + + if not has_error then + if a_is_reusing_connection then + -- Restore normal recv timeout! + a_socket.set_recv_timeout (socket_recv_timeout) -- FIXME: return a 408 Request Timeout response .. + end + from + line := next_line (a_socket) + until + line = Void or end_of_stream or has_error + loop + n := line.count + debug ("ew_standalone") + if l_is_verbose then + log (line, debug_level) + end + 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 + -- Except for HTTP/1.0, persistent connection is the default. + is_persistent_connection_requested := True + if is_http_version_1_0 then + is_persistent_connection_requested := attached request_header_map.item ("Connection") as l_connection and then + l_connection.is_case_insensitive_equal_general ("keep-alive") + else + -- By default HTTP:1/1 support persistent connection. + if attached request_header_map.item ("Connection") as l_connection then + if l_connection.is_case_insensitive_equal_general ("close") then + is_persistent_connection_requested := False + end + else + is_persistent_connection_requested := True + end + end + end else report_error ("Bad header line (empty)") end else report_error ("Socket is not readable") end - l_is_verbose := is_verbose - if not has_error then - from - line := next_line (a_socket) - until - line = Void or end_of_stream or has_error - loop - n := line.count - debug ("ew_standalone") - if l_is_verbose then - log (line, debug_level) - end - 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 - -- Except for HTTP/1.0, persistent connection is the default. - is_persistent_connection_requested := True - if is_http_version_1_0 then - is_persistent_connection_requested := attached request_header_map.item ("Connection") as l_connection and then - l_connection.is_case_insensitive_equal_general ("keep-alive") - else - -- By default HTTP:1/1 support persistent connection. - if attached request_header_map.item ("Connection") as l_connection then - if l_connection.is_case_insensitive_equal_general ("close") then - is_persistent_connection_requested := False - end - else - is_persistent_connection_requested := True - end - end - end end analyze_request_line (line: STRING) - -- Analyze `line' as a HTTP request line + -- Analyze `line' as a HTTP request line. + -- note: may update `has_error'. require valid_line: line /= Void and then not line.is_empty local @@ -488,21 +497,19 @@ feature -- Parsing next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING -- Next line fetched from `a_socket' is available. + -- note: may update `has_error'. require - not_has_error: not has_error or is_verbose + not_has_error: not has_error is_readable: a_socket.is_open_read local retried: BOOLEAN do if retried then report_error ("Rescue in next_line") + a_socket.close Result := Void - elseif - a_socket.readable and then - socket_has_incoming_data (a_socket) - then - - a_socket.read_line_thread_aware + elseif a_socket.readable then + a_socket.read_line_noexception Result := a_socket.last_string -- Do no check `socket_ok' before socket operation, -- otherwise it may be False, due to error during other socket operation in same thread. @@ -520,6 +527,7 @@ feature -- Parsing end end rescue + -- In case of network error exception (as EiffelNet reports error raising exception) retried := True retry end @@ -558,7 +566,9 @@ feature {NONE} -- Helpers a_socket.readable do -- FIXME: check if both are really needed. - Result := a_socket.ready_for_reading and then a_socket.has_incoming_data +-- Result := a_socket.ready_for_reading --and then a_socket.has_incoming_data +-- Result := a_socket.has_incoming_data + Result := True end invariant diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/from_16_11/tcp_stream_socket_ext.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/from_16_11/tcp_stream_socket_ext.e deleted file mode 100644 index 65d4d70a..00000000 --- a/library/server/ewsgi/connectors/standalone/lib/httpd/network/from_16_11/tcp_stream_socket_ext.e +++ /dev/null @@ -1,9 +0,0 @@ -note - description: "[ - Since 16.11, the EiffelNet socket interface has recv_timeout and send_timeout. - ]" - -deferred class - TCP_STREAM_SOCKET_EXT - -end diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/httpd_stream_socket.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/httpd_stream_socket.e index 7532288c..b916b2f5 100644 --- a/library/server/ewsgi/connectors/standalone/lib/httpd/network/httpd_stream_socket.e +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/network/httpd_stream_socket.e @@ -1,369 +1,235 @@ note - description: "[ - Summary description for {HTTPD_STREAM_SOCKET} - that can be used for http or https connection. - ]" + description: "Summary description for {HTTPD_STREAM_SOCKET}." date: "$Date$" revision: "$Revision$" class HTTPD_STREAM_SOCKET +inherit + NETWORK_STREAM_SOCKET + + HTTPD_STREAM_SOCKET_EXT + create - make_server_by_address_and_port, - make_server_by_port, - make_client_by_address_and_port, - make_client_by_port, - make_from_separate, - make_empty + make, make_empty, + make_client_by_port, make_client_by_address_and_port, + make_server_by_port, make_server_by_address_and_port, make_loopback_server_by_port -create {HTTPD_STREAM_SOCKET} - make +create {NETWORK_STREAM_SOCKET} + make_from_descriptor_and_address -feature {NONE} -- Initialization +feature -- Input - make_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER) + read_character_noexception + -- Read a new character. + -- Make result available in `last_character'. + -- No exception raised! do - create {TCP_STREAM_SOCKET} socket.make_server_by_address_and_port (an_address, a_port) + read_to_managed_pointer_noexception (socket_buffer, 0, character_8_bytes) + if bytes_read /= character_8_bytes then + socket_error := "Peer closed connection" + else + last_character := socket_buffer.read_character (0) + socket_error := Void + end end - make_server_by_port (a_port: INTEGER) + read_stream_noexception (nb_char: INTEGER) + -- Read a string of at most `nb_char' characters. + -- Make result available in `last_string'. + local + ext: C_STRING + return_val: INTEGER do - create {TCP_STREAM_SOCKET} socket.make_server_by_port (a_port) + create ext.make_empty (nb_char + 1) + return_val := c_read_stream_noexception (descriptor, nb_char, ext.item) + bytes_read := return_val + if return_val >= 0 then + ext.set_count (return_val) + last_string := ext.substring (1, return_val) + else + socket_error := "Peer error [0x" + return_val.to_hex_string + "]" + last_string.wipe_out + end end - make_client_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER) + read_to_managed_pointer_noexception (p: MANAGED_POINTER; start_pos, nb_bytes: INTEGER) + -- Read at most `nb_bytes' bound bytes and make result + -- available in `p' at position `start_pos'. + -- No exception raised! do - create {TCP_STREAM_SOCKET} socket.make_client_by_address_and_port (an_address, a_port) + read_into_pointer_noexception (p.item, start_pos, nb_bytes) end - make_client_by_port (a_peer_port: INTEGER; a_peer_host: STRING) + read_line_noexception + -- Read a line of characters (ended by a new_line). + -- No exception raised! + local + l_last_string: like last_string do - create {TCP_STREAM_SOCKET} socket.make_client_by_port (a_peer_port, a_peer_host) + create l_last_string.make (512) + read_character_noexception + from + until + last_character = '%N' or else was_error + loop + l_last_string.extend (last_character) + read_character_noexception + end + last_string := l_last_string end - make_from_separate (s: separate HTTPD_STREAM_SOCKET) + peek_stream_noexception (nb_char: INTEGER) + -- Read a string of at most `nb_char' characters without removing the data from the queue. + -- Make result available in last_string. + -- No exception raised! require - descriptor_available: s.descriptor_available + readable: readable + socket_exists: exists + local + ext: C_STRING + retval: INTEGER + l: like last_string do - create {TCP_STREAM_SOCKET} socket.make_from_separate (s.socket) - end - - make_empty - do - create {TCP_STREAM_SOCKET} socket.make_empty - end - - retrieve_socket (s: HTTPD_STREAM_SOCKET): INTEGER - do - Result := s.socket.descriptor - end - -feature -- Change - - set_timeout (n: INTEGER) - -- Set timeout to `n' seconds. - do - if attached {NETWORK_STREAM_SOCKET} socket as l_socket then - l_socket.set_timeout (n) + create ext.make_empty (nb_char + 1) + retval := c_recv_noexception (descriptor, ext.item, nb_char, c_peekmsg) + if retval = 0 then + last_string.wipe_out + socket_error := Void + elseif retval > 0 then + ext.set_count (retval) + l := last_string + l.wipe_out + l.grow (retval) + l.set_count (retval) + ext.read_substring_into (l, 1, retval) + socket_error := Void + else + last_string.wipe_out + socket_error := "Socket error (MSG_PEEK)" end + ensure + last_string_not_void: last_string /= Void end - set_connect_timeout (n: INTEGER) - do - if attached {NETWORK_STREAM_SOCKET} socket as l_socket then - l_socket.set_connect_timeout (n) - end - end +feature {NONE} -- Input - set_accept_timeout (n: INTEGER) - do - if attached {NETWORK_STREAM_SOCKET} socket as l_socket then - l_socket.set_accept_timeout (n) - end - end - - set_recv_timeout (a_timeout_seconds: INTEGER) - -- Set the receive timeout in seconds on Current socket. - do - if attached {TCP_STREAM_SOCKET} socket as l_socket then - l_socket.set_recv_timeout (a_timeout_seconds) - end - end - - set_send_timeout (a_timeout_seconds: INTEGER) - -- Set the send timeout in seconds on Current socket. - do - if attached {TCP_STREAM_SOCKET} socket as l_socket then - l_socket.set_send_timeout (a_timeout_seconds) - end - 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 - - peek_stream (nb_char: INTEGER) + read_into_pointer_noexception (p: POINTER; start_pos, nb_bytes: INTEGER_32) + -- Read at most `nb_bytes' bound bytes and make result + -- available in `p' at position `start_pos'. + -- No exception raised! require - nb_char_positive: nb_char > 0 + p_not_void: p /= default_pointer + nb_bytes_non_negative: nb_bytes >= 0 + is_readable: readable + local + l_read: INTEGER_32 + l_last_read: INTEGER_32 do - if attached {TCP_STREAM_SOCKET} socket as l_socket then - l_socket.peek_stream (nb_char) + from + l_last_read := 1 + until + l_read = nb_bytes or l_last_read <= 0 + loop + l_last_read := c_read_stream_noexception (descriptor, nb_bytes - l_read, p + start_pos + l_read) + if l_last_read >= 0 then + l_read := l_read + l_last_read + end end - end - - bytes_read: INTEGER - do - Result := socket.bytes_read + bytes_read := l_read + ensure + bytes_read_updated: 0 <= bytes_read and bytes_read <= nb_bytes end feature -- Output - send_message (a_msg: STRING) + bytes_sent: INTEGER + -- Last number of bytes sent by `put_managed_pointer_noexception' (i.e `put_pointer_content_noexception'). + + put_managed_pointer_noexception (p: MANAGED_POINTER; start_pos, nb_bytes: INTEGER_32) + -- Put data of length `nb_bytes' pointed by `start_pos' index in `p' at + -- current position. + -- Update `bytes_sent'. + -- No exception raised! + require + p_not_void: p /= Void + p_large_enough: p.count >= nb_bytes + start_pos + nb_bytes_non_negative: nb_bytes >= 0 + extendible: extendible do - put_string (a_msg) + put_pointer_content_noexception (p.item, start_pos, nb_bytes) + end + + put_pointer_content_noexception (a_pointer: POINTER; a_offset, a_byte_count: INTEGER) + -- Write `a_byte_count' bytes to the socket. + -- The data is taken from the memory area pointed to by `a_pointer', at offset `a_offset'. + -- Update `bytes_sent'. + -- No exception raised! + require + pointer_not_void: a_pointer /= default_pointer + byte_count_non_negative: a_byte_count >= 0 + extendible: extendible + local + l_sent: INTEGER_32 + l_last_sent: INTEGER_32 + do + from + l_last_sent := 1 + until + l_sent = a_byte_count or l_last_sent <= 0 + loop + l_last_sent := c_put_stream_noexception (descriptor, a_pointer + a_offset + l_sent, a_byte_count - l_sent) + if l_last_sent >= 0 then + l_sent := l_sent + l_last_sent + elseif l_sent < a_byte_count then + socket_error := "No all bytes sent!" + end + end + bytes_sent := l_sent + ensure + bytes_sent_updated: not was_error implies (0 <= bytes_sent and bytes_sent <= a_byte_count) + end + + put_character_noexception (c: CHARACTER) + -- Write character `c' to socket. + do + socket_buffer.put_character (c, 0) + put_managed_pointer_noexception (socket_buffer, 0, character_8_bytes) + end + + put_readable_string_8_noexception (s: READABLE_STRING_8) + -- Write readable string `s' to socket. + -- No exception raised! + local + ext: C_STRING + do + create ext.make (s) + put_managed_pointer_noexception (ext.managed_data, 0, s.count) end put_readable_string_8 (s: READABLE_STRING_8) -- Write readable string `s' to socket. + local + ext: C_STRING 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 + create ext.make (s) + put_managed_pointer (ext.managed_data, 0, s.count) 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 - - exists: BOOLEAN - do - Result := socket.exists - 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 - - is_connected: BOOLEAN - do - if attached {TCP_STREAM_SOCKET} socket as l_socket then - Result := l_socket.is_connected - end - end - - is_created: BOOLEAN - do - if attached {NETWORK_SOCKET} socket as l_socket then - Result := l_socket.is_created - 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 - - connect - do - socket.connect - end - - close - do - socket.close - end - - listen (a_queue: INTEGER) - do - socket.listen (a_queue) - end - - accept - do - socket.accept - end - - accept_to (other: separate HTTPD_STREAM_SOCKET) - -- Accept a new connection on listen socket. - -- Socket of accepted connection is available in `other'. - do - if - attached {NETWORK_STREAM_SOCKET} socket as l_socket and then - attached {separate NETWORK_STREAM_SOCKET} other.socket as l_other_socket - then - l_socket.accept_to (l_other_socket) - end - end - - set_blocking - do - socket.set_blocking - end - - set_non_blocking - do - socket.set_non_blocking - end - - readable: BOOLEAN - do - Result := socket.readable - end +feature -- Status report has_incoming_data: BOOLEAN -- Check if Current has available data to be read. -- note: no data will not be removed from the queue. + require + socket_exists: exists do - if attached {TCP_STREAM_SOCKET} socket as l_socket then - Result := l_socket.has_incoming_data - end + peek_stream_noexception (1) + Result := last_string.count = 1 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 {NETWORK_STREAM_SOCKET} 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 - - network_stream_socket: detachable NETWORK_STREAM_SOCKET - do - if attached {NETWORK_STREAM_SOCKET} socket as s then - Result := s - end - end - -;note +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: "[ @@ -373,4 +239,5 @@ feature {HTTPD_STREAM_SOCKET} -- Implementation Website http://www.eiffel.com Customer support http://support.eiffel.com ]" + end diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/httpd_stream_socket_ext.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/httpd_stream_socket_ext.e new file mode 100644 index 00000000..0598f873 --- /dev/null +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/network/httpd_stream_socket_ext.e @@ -0,0 +1,13 @@ +note + description: "[ + Extension to HTTPD_STREAM_SOCKET to support backward compatibility. + + TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD. + ]" + +deferred class + HTTPD_STREAM_SOCKET_EXT + +feature {NONE} -- No-Exception network operation + +end diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/httpd_stream_ssl_socket.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/httpd_stream_ssl_socket.e index 1217d0d1..15435601 100644 --- a/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/httpd_stream_ssl_socket.e +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/httpd_stream_ssl_socket.e @@ -1,8 +1,5 @@ note - description: "[ - Summary description for {HTTPD_STREAM_SSL_SOCKET} - that can be used for http or https connection. - ]" + description: "SSL tcp stream socket." date: "$Date$" revision: "$Revision$" @@ -11,159 +8,142 @@ class inherit HTTPD_STREAM_SOCKET + undefine + make_empty, make_from_descriptor_and_address, + error_number, + readstream, read_stream, + read_into_pointer, + read_to_managed_pointer, + put_pointer_content, + write, send, + close_socket, + connect, shutdown, + do_accept redefine - port, - is_bound, - ready_for_writing, - ready_for_reading, - try_ready_for_reading, - put_readable_string_8, - make_empty + put_managed_pointer, + read_stream_noexception, + read_into_pointer_noexception, + put_pointer_content_noexception end + SSL_NETWORK_STREAM_SOCKET + redefine + put_managed_pointer -- Redefine to allow support of compiler before 16.11. + end + + HTTPD_STREAM_SSL_SOCKET_EXT + create - make_ssl_server_by_address_and_port, make_ssl_server_by_port, - make_server_by_address_and_port, make_server_by_port, - make_ssl_client_by_address_and_port, make_ssl_client_by_port, - make_client_by_address_and_port, make_client_by_port, - make_empty + make, make_empty, + make_client_by_port, make_client_by_address_and_port, + make_server_by_port, make_server_by_address_and_port, make_loopback_server_by_port -create {HTTPD_STREAM_SOCKET} - make +create {SSL_NETWORK_STREAM_SOCKET} + make_from_descriptor_and_address -feature {NONE} -- Initialization +feature -- Input - make_ssl_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL) + read_stream_noexception (nb_char: INTEGER) + -- Read a string of at most `nb_char' characters. + -- Make result available in `last_string'. local - l_socket: SSL_TCP_STREAM_SOCKET + ext: C_STRING + return_val: INTEGER 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_fn, a_key_fn) + if + attached context as l_context and then + attached l_context.last_ssl as l_ssl + then + create ext.make_empty (nb_char + 1) + return_val := l_ssl.read (ext.item , nb_char) + bytes_read := return_val + if return_val >= 0 then + ext.set_count (return_val) + last_string := ext.substring (1, return_val) + else + socket_error := "Peer error [0x" + return_val.to_hex_string + "]" + last_string.wipe_out + end + else + check has_context: False end + end end - make_ssl_server_by_port (a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL) - 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_fn, a_key_fn) - end +feature {NONE} -- Input - make_ssl_client_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL) + read_into_pointer_noexception (p: POINTER; start_pos, nb_bytes: INTEGER_32) + -- Read at most `nb_bytes' bound bytes and make result + -- available in `p' at position `start_pos'. + -- No exception raised! local - l_socket: SSL_TCP_STREAM_SOCKET + l_read: INTEGER + l_last_read: INTEGER do - create l_socket.make_client_by_address_and_port (an_address, a_port) - l_socket.set_tls_protocol (a_ssl_protocol) - socket := l_socket - set_certificates (a_crt_fn, a_key_fn) - end - - make_ssl_client_by_port (a_peer_port: INTEGER; a_peer_host: STRING; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL) - local - l_socket: SSL_TCP_STREAM_SOCKET - do - create l_socket.make_client_by_port (a_peer_port, a_peer_host) - l_socket.set_tls_protocol (a_ssl_protocol) - socket := l_socket - set_certificates (a_crt_fn, a_key_fn) - end - - make_empty - -- . - do - create {SSL_TCP_STREAM_SOCKET} socket.make_empty + if + attached context as l_context and then + attached l_context.last_ssl as l_ssl + then + from + l_last_read := 1 + until + l_read = nb_bytes or l_last_read <= 0 + loop + l_last_read := l_ssl.read (p + start_pos + l_read, nb_bytes - l_read) + if l_last_read >= 0 then + l_read := l_read + l_last_read + end + end + bytes_read := l_read + else + check has_context: False end + end end feature -- Output - put_readable_string_8 (s: READABLE_STRING_8) - -- + put_managed_pointer (p: MANAGED_POINTER; start_pos, nb_bytes: INTEGER) + -- Put data of length `nb_bytes' pointed by `start_pos' index in `p' at + -- current position. 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 + Precursor {HTTPD_STREAM_SOCKET} (p, start_pos, nb_bytes) end -feature -- Status Report - - port: INTEGER - -- + put_pointer_content_noexception (a_pointer: POINTER; a_offset, a_byte_count: INTEGER) + -- Write `a_byte_count' bytes to the socket. + -- The data is taken from the memory area pointed to by `a_pointer', at offset `a_offset'. + -- Update `bytes_sent'. + -- No exception raised! + local + l_bytes_sent: 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_filename, a_key_filename: detachable READABLE_STRING_GENERAL) - do - if attached {SSL_NETWORK_STREAM_SOCKET} socket as l_socket then - if a_crt_filename /= Void then - l_socket.set_certificate_file_name (a_crt_filename) - end - if a_key_filename /= Void then - l_socket.set_key_file_name (a_key_filename) + if + attached context as l_context and then + attached l_context.last_ssl as l_ssl + then + l_bytes_sent := ssl_write (l_ssl, a_pointer + a_offset, a_byte_count) + if l_bytes_sent < a_byte_count then + socket_error := "No all bytes sent!" end + bytes_sent := l_bytes_sent + else + check has_last_ssl: False end + end + end + +feature -- Element change + + set_certificate_filenames (a_crt_filename, a_key_filename: detachable READABLE_STRING_GENERAL) + do + if a_crt_filename /= Void then + set_certificate_file_name (a_crt_filename) + end + if a_key_filename /= Void then + set_key_file_name (a_key_filename) end end note - copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2013, Javier Velilla, Jocelyn Fiat 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/standalone/lib/httpd/network/ssl/httpd_stream_ssl_socket_ext.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/httpd_stream_ssl_socket_ext.e new file mode 100644 index 00000000..e8f095e8 --- /dev/null +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/httpd_stream_ssl_socket_ext.e @@ -0,0 +1,23 @@ +note + description: "[ + Extension to HTTPD_STREAM_SOCKET to support backward compatibility. + + TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD. + ]" + +deferred class + HTTPD_STREAM_SSL_SOCKET_EXT + +feature {NONE} -- SSL bridge + + ssl_write (a_ssl: SSL; a_pointer: POINTER; a_byte_count: INTEGER): INTEGER + do + Result := a_ssl.write (a_pointer, a_byte_count) + if a_ssl.was_error then + if Result >= 0 then + Result := -1 + end + end + end + +end diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/ssl_tcp_stream_socket.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/ssl_tcp_stream_socket.e deleted file mode 100644 index de71f479..00000000 --- a/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/ssl_tcp_stream_socket.e +++ /dev/null @@ -1,73 +0,0 @@ -note - description: "SSL tcp stream socket." - 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, - make_client_by_address_and_port, make_client_by_port, - make_empty - -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/standalone/lib/httpd/network/tcp_stream_socket.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/tcp_stream_socket.e deleted file mode 100644 index 2ea98a09..00000000 --- a/library/server/ewsgi/connectors/standalone/lib/httpd/network/tcp_stream_socket.e +++ /dev/null @@ -1,153 +0,0 @@ -note - description: "Summary description for {TCP_STREAM_SOCKET}." - date: "$Date$" - revision: "$Revision$" - -class - TCP_STREAM_SOCKET - -inherit - NETWORK_STREAM_SOCKET - redefine - make - end - - TCP_STREAM_SOCKET_EXT - -create - make_server_by_address_and_port, - make_server_by_port, - make_client_by_address_and_port, - make_client_by_port, - make_from_separate, - make_empty - -create {NETWORK_STREAM_SOCKET} - make_from_descriptor_and_address - -feature {NONE} -- Initialization - - make - -- Create a network stream socket. - do - Precursor - debug - set_reuse_address - end - 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 - - peek_stream (nb_char: INTEGER) - -- Read a string of at most `nb_char' characters without removing the data from the queue. - -- Make result available in last_string. - require - readable: readable - socket_exists: exists - local - ext: C_STRING - retval: INTEGER - l: like last_string - do - create ext.make_empty (nb_char + 1) - retval := clib_recv (descriptor, ext.item, nb_char, c_peekmsg) - if retval = 0 then - last_string.wipe_out - socket_error := Void - elseif retval > 0 then - ext.set_count (retval) - l := last_string - l.wipe_out - l.grow (retval) - l.set_count (retval) - ext.read_substring_into (l, 1, retval) - socket_error := Void - else - last_string.wipe_out - socket_error := "Socket error (MSG_PEEK)" - end - ensure - last_string_not_void: last_string /= Void - end - - 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 - - has_incoming_data: BOOLEAN - -- Check if Current has available data to be read. - -- note: no data will not be removed from the queue. - require - socket_exists: exists - do - peek_stream (1) - Result := last_string.count = 1 - end - - 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} -- C implementation - - clib_recv (a_fd: INTEGER; buf: POINTER; len: INTEGER; flags: INTEGER): INTEGER - -- External routine to receive at most `len' number of - -- bytes into buffer `buf' from socket `fd' with `flags' options. - external - "C inline" - alias - "[ - return (EIF_INTEGER) recv ((int) $a_fd, (char *) $buf, (int) $len, (int) $flags); - ]" - 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/standalone/lib/httpd/network/until_16_05/httpd_stream_socket_ext.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/httpd_stream_socket_ext.e new file mode 100644 index 00000000..188ac076 --- /dev/null +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/httpd_stream_socket_ext.e @@ -0,0 +1,160 @@ +note + description: "[ + Until 16.05, the EiffelNet socket interface DOES NOT have + - make_server_by_address_and_port + - recv_timeout + - send_timeout. + + TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD. + ]" + +deferred class + HTTPD_STREAM_SOCKET_EXT + +inherit + PLATFORM + +feature -- Initialization + + make + deferred + end + + make_server_by_address_and_port (a_address: INET_ADDRESS; a_port: INTEGER) + -- Create server socket on `a_address' and `a_port'. + require + valid_port: a_port >= 0 + do + make + set_address (create {like address_type}.make_from_address_and_port (a_address, a_port)) + bind + end + +feature -- Basic operation + + bind + deferred + end + +feature -- Access + + set_address (addr: detachable like address_type) + deferred + end + + address_type: NETWORK_SOCKET_ADDRESS + deferred + end + + descriptor: INTEGER + -- Socket descriptor of current socket + deferred + end + +feature -- Socket Recv and Send timeout. + + set_recv_timeout (a_timeout_seconds: INTEGER) + -- Set the receive timeout in seconds on Current socket. + -- if `0' the related operations will never timeout. + require + positive_timeout: a_timeout_seconds >= 0 + do + c_set_sock_recv_timeout (descriptor, level_sol_socket, a_timeout_seconds) + end + + set_send_timeout (a_timeout_seconds: INTEGER) + -- Set the send timeout in milliseconds on Current socket. + -- if `0' the related operations will never timeout. + require + positive_timeout: a_timeout_seconds >= 0 + do + c_set_sock_send_timeout (descriptor, level_sol_socket, a_timeout_seconds) + end + +feature {NONE} -- Externals + + level_sol_socket: INTEGER + -- SOL_SOCKET level of options + deferred + end + + c_set_sock_recv_timeout (a_fd, a_level: INTEGER; a_timeout_seconds: INTEGER) + -- C routine to set socket option `SO_RCVTIMEO' with `a_timeout_seconds' seconds. + external + "C inline use %"ew_httpd_net.h%"" + alias + "[ +#ifdef SO_RCVTIMEO + int flag = SO_RCVTIMEO; +#else + int flag = 0x1006; +#endif + +#ifdef EIF_WINDOWS + int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */ + setsockopt((int) $a_fd, (int) $a_level, flag, (char *) &arg, sizeof(arg)); +#else + struct timeval tv; + tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */ + tv.tv_usec = 0; + setsockopt((int) $a_fd, (int) $a_level, flag, (struct timeval *)&tv, sizeof(struct timeval)); +#endif + ]" + end + + c_set_sock_send_timeout (a_fd, a_level: INTEGER; a_timeout_seconds: INTEGER) + -- C routine to set socket option `SO_SNDTIMEO' with `a_timeout_seconds' seconds. + external + "C inline use %"ew_httpd_net.h%"" + alias + "[ +#ifdef SO_RCVTIMEO + int flag = SO_SNDTIMEO; +#else + int flag = 0x1005; +#endif +#ifdef EIF_WINDOWS + int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */ + setsockopt((int) $a_fd, (int) $a_level, flag, (char *) &arg, sizeof(arg)); +#else + struct timeval tv; + tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */ + tv.tv_usec = 0; + setsockopt((int) $a_fd, (int) $a_level, flag, (struct timeval *)&tv, sizeof(struct timeval)); +#endif + ]" + end + +feature {NONE} -- No-Exception network operation + + c_recv_noexception (a_fd: INTEGER; buf: POINTER; len: INTEGER; flags: INTEGER): INTEGER + -- External routine to read a `len' number of characters + -- into buffer `buf' from socket `a_fd' with options `flags'. + external + "C inline use %"ew_httpd_net.h%"" + alias + "[ + recv((int) $a_fd, (char *) $buf, (int) $len, (int) $flags) + ]" + end + + c_read_stream_noexception (a_fd: INTEGER; len: INTEGER; buf: POINTER): INTEGER + -- External routine to read a `len' number of characters + -- into buffer `buf' from socket `a_fd'. + do + Result := c_recv_noexception (a_fd, buf, len, 0) + end + + c_put_stream_noexception (a_fd: INTEGER; buf: POINTER; len: INTEGER): INTEGER + -- External routine to write stream pointed by `s' of + -- length `length' to socket `fd'. + -- Note: does not raise exception on error, but return error value as Result. + external + "C inline use %"ew_httpd_net.h%"" + alias + "[ + send((int) $a_fd, (char *) $buf, (int) $len, (int) 0) + ]" + end + +end diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/ssl/httpd_stream_ssl_socket_ext.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/ssl/httpd_stream_ssl_socket_ext.e new file mode 100644 index 00000000..4fa6ac64 --- /dev/null +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/ssl/httpd_stream_ssl_socket_ext.e @@ -0,0 +1,41 @@ +note + description: "[ + Extension to HTTPD_STREAM_SOCKET to support backward compatibility. + + TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD. + ]" + +deferred class + HTTPD_STREAM_SSL_SOCKET_EXT + +feature {NONE} -- SSL bridge + + ssl_write (a_ssl: SSL; a_pointer: POINTER; a_byte_count: INTEGER): INTEGER + do + -- In delivery until 16.05 + -- SSL.write does not return any value! + -- So let's use `c_ssl_write' from Current class + -- instead of: + -- a_ssl.write (a_pointer, a_byte_count) + + Result := c_ssl_write (a_ssl.ptr, a_pointer, a_byte_count) + if a_ssl.was_error then + -- Until 16.05, there is no error check for `SSL.write' + -- so nothing can be done here. + + if Result >= 0 then + Result := -1 + end + end + end + + c_ssl_write (an_ssl_ptr: POINTER; buffer: POINTER; nb_bytes: INTEGER_32): INTEGER_32 + -- External call to SSL_write + -- (export status {NONE}) + external + "C use %"eif_openssl.h%"" + alias + "SSL_write" + end + +end diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/tcp_stream_socket_ext.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/tcp_stream_socket_ext.e deleted file mode 100644 index 22ebf01d..00000000 --- a/library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/tcp_stream_socket_ext.e +++ /dev/null @@ -1,95 +0,0 @@ -note - description: "[ - Until 16.05, the EiffelNet socket interface DOES NOT have recv_timeout and send_timeout. - ]" - -deferred class - TCP_STREAM_SOCKET_EXT - -feature -- Access - - descriptor: INTEGER - -- Socket descriptor of current socket - deferred - end - -feature -- Socket Recv and Send timeout. - --- recv_timeout: INTEGER --- -- Receive timeout in seconds on Current socket. --- do --- Result := c_get_sock_recv_timeout (descriptor, level_sol_socket) --- ensure --- result_not_negative: Result >= 0 --- end --- --- send_timeout: INTEGER --- -- Send timeout in seconds on Current socket. --- do --- Result := c_get_sock_send_timeout (descriptor, level_sol_socket) --- ensure --- result_not_negative: Result >= 0 --- end - - set_recv_timeout (a_timeout_seconds: INTEGER) - -- Set the receive timeout in seconds on Current socket. - -- if `0' the related operations will never timeout. - require - positive_timeout: a_timeout_seconds >= 0 - do - c_set_sock_recv_timeout (descriptor, level_sol_socket, a_timeout_seconds) - end - - set_send_timeout (a_timeout_seconds: INTEGER) - -- Set the send timeout in milliseconds on Current socket. - -- if `0' the related operations will never timeout. - require - positive_timeout: a_timeout_seconds >= 0 - do - c_set_sock_send_timeout (descriptor, level_sol_socket, a_timeout_seconds) - end - -feature {NONE} -- Externals - - level_sol_socket: INTEGER - -- SOL_SOCKET level of options - deferred - end - - c_set_sock_recv_timeout (a_fd, a_level: INTEGER; a_timeout_seconds: INTEGER) - -- C routine to set socket option `SO_RCVTIMEO' with `a_timeout_seconds' seconds. - external - "C inline" - alias - "[ -#ifdef EIF_WINDOWS - int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */ - setsockopt((SOCKET) $a_fd, (int) $a_level, (int) SO_RCVTIMEO, (char *) &arg, sizeof(arg)); -#else - struct timeval tv; - tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */ - - setsockopt((int) $a_fd, (int) $a_level, (int) SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); -#endif - ]" - end - - c_set_sock_send_timeout (a_fd, a_level: INTEGER; a_timeout_seconds: INTEGER) - -- C routine to set socket option `SO_SNDTIMEO' with `a_timeout_seconds' seconds. - external - "C inline" - alias - "[ -#ifdef EIF_WINDOWS - int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */ - setsockopt((SOCKET) $a_fd, (int) $a_level, (int) SO_SNDTIMEO, (char *) &arg, sizeof(arg)); -#else - struct timeval tv; - tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */ - - setsockopt((int) $a_fd, (int) $a_level, (int) SO_SNDTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); -#endif - ]" - end - -end diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/spec/include_until_16_05/ew_httpd_net.h b/library/server/ewsgi/connectors/standalone/lib/httpd/spec/include_until_16_05/ew_httpd_net.h new file mode 100644 index 00000000..ed002f5b --- /dev/null +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/spec/include_until_16_05/ew_httpd_net.h @@ -0,0 +1,52 @@ +/* +indexing + description: "Functions used by the EiffelWeb httpd networking classes. " + copyright: "Copyright (c) 2011-2016, Jocelyn Fiat, 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 + ]" +*/ + +#ifndef _ew_httpd_net_h_ +#define _ew_httpd_net_h_ + +#include "eif_config.h" + +#ifdef EIF_WINDOWS +# ifndef _WINSOCKAPI_ +# define FD_SETSIZE 256 +# include +# include +# include +# endif +#else /* unix-specific */ +# include +# include +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +/* extern declarations ... */ +#ifdef EIF_WINDOWS +extern int setsockopt(int, int, int, char*, int); +extern int recv(int, char *, int, int); +extern int send(int, char *, int, int); +#else +extern int setsockopt(int, int, int, (struct timeval*), int); +extern int recv(int, char *, int, int); +extern int send(int, char *, int, int); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_server.e b/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_server.e index 4045aa29..e1b5bca0 100644 --- a/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_server.e +++ b/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_server.e @@ -20,13 +20,19 @@ create feature {NONE} -- Factory new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTPD_STREAM_SOCKET + local + s_ssl: HTTPD_STREAM_SSL_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) + create s_ssl.make_server_by_address_and_port (a_addr, a_http_port) + Result := s_ssl else - create {HTTPD_STREAM_SSL_SOCKET} Result.make_ssl_server_by_port (a_http_port, configuration.ssl_protocol, configuration.ca_crt, configuration.ca_key) + create s_ssl.make_server_by_port (a_http_port) end + s_ssl.set_tls_protocol (configuration.ssl_protocol) + s_ssl.set_certificate_filenames (configuration.ca_crt, configuration.ca_key) + Result := s_ssl else Result := Precursor (a_addr, a_http_port) end diff --git a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_connector.e b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_connector.e index 0d227df9..19f2cbe9 100644 --- a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_connector.e +++ b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_connector.e @@ -35,10 +35,10 @@ feature {NONE} -- Initialization initialize_server (server) end - make_with_base (a_base: like base) + make_with_base (a_base: detachable separate READABLE_STRING_8) -- Create current standalone connector with base url `a_base' require - a_base_starts_with_slash: (a_base /= Void and then not a_base.is_empty) implies a_base.starts_with ("/") + a_base_starts_with_slash: a_base /= Void implies is_valid_base (a_base) do make set_base (a_base) @@ -109,7 +109,8 @@ feature -- Status report feature -- Callbacks on_launched_actions: ACTION_SEQUENCE [TUPLE [WGI_STANDALONE_CONNECTOR [WGI_EXECUTION]]] - -- Actions triggered when launched + -- Actions triggered when launched. + -- WARNING: only supported for now with SCOOP concurrency mode. [2016-oct-07] feature -- Event @@ -123,16 +124,25 @@ feature -- Event feature -- Element change - set_base (v: like base) + set_base (v: detachable separate READABLE_STRING_8) -- Set base url `base' to `v'. require - b_starts_with_slash: (v /= Void and then not v.is_empty) implies v.starts_with ("/") + b_starts_with_slash: (v /= Void and then not v.is_empty) implies is_valid_base (v) do - base := v + if v = Void then + base := Void + else + create {STRING_8} base.make_from_separate (v) + end ensure valid_base: (attached base as l_base and then not l_base.is_empty) implies l_base.starts_with ("/") end + is_valid_base (v: separate READABLE_STRING_8): BOOLEAN + do + Result := not v.is_whitespace and then v[1] = '/' + end + set_port_number (a_port_number: INTEGER) -- Set port number to `a_port_number'. require @@ -141,6 +151,13 @@ feature -- Element change set_port_on_configuration (a_port_number, configuration) end + set_socket_recv_timeout (a_nb_seconds: INTEGER) + require + a_nb_seconds_positive_or_zero: a_nb_seconds >= 0 + do + set_socket_recv_timeout_on_configuration (a_nb_seconds, configuration) + end + set_max_concurrent_connections (nb: INTEGER) -- Set maximum concurrent connections to `nb'. require @@ -189,12 +206,12 @@ feature -- Events require obs.started -- SCOOP wait condition. do + -- FIXME: this works only with SCOOP concurrency mode. [2016-oct-07] if obs.port > 0 then on_launched (obs.port) end end - feature {NONE} -- Implementation server_controller (a_server: like server): separate HTTPD_CONTROLLER @@ -238,6 +255,11 @@ feature {NONE} -- Implementation: element change cfg.set_http_server_port (a_port_number) end + set_socket_recv_timeout_on_configuration (a_nb_seconds: INTEGER; cfg: like configuration) + do + cfg.set_socket_recv_timeout (a_nb_seconds) + end + set_max_concurrent_connections_on_configuration (nb: INTEGER; cfg: like configuration) do cfg.set_max_concurrent_connections (nb) diff --git a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_output_stream.e b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_output_stream.e index b3656cbb..2652576f 100644 --- a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_output_stream.e +++ b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_output_stream.e @@ -75,23 +75,23 @@ feature -- Output -- Send `s' to http client do last_target_call_succeed := False - target.put_readable_string_8 (s) - last_target_call_succeed := True + target.put_readable_string_8_noexception (s) + last_target_call_succeed := not target.was_error end put_string (s: READABLE_STRING_8) -- Send `s' to http client do last_target_call_succeed := False - target.put_readable_string_8 (s) - last_target_call_succeed := True + target.put_readable_string_8_noexception (s) + last_target_call_succeed := not target.was_error end put_character (c: CHARACTER_8) do last_target_call_succeed := False target.put_character (c) - last_target_call_succeed := True + last_target_call_succeed := not target.was_error end feature -- Status report @@ -116,7 +116,7 @@ feature -- Basic operations end note - copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" + copyright: "2011-2016, 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/wsf/connector/standalone/wsf_standalone_service_launcher.e b/library/server/wsf/connector/standalone/wsf_standalone_service_launcher.e index e4169605..ca1c6182 100644 --- a/library/server/wsf/connector/standalone/wsf_standalone_service_launcher.e +++ b/library/server/wsf/connector/standalone/wsf_standalone_service_launcher.e @@ -64,7 +64,7 @@ feature {NONE} -- Initialization verbose := False verbose_level := notice_level - base_url := "" + base_url := Void if attached options as opts then if attached {READABLE_STRING_GENERAL} opts.option ("server_name") as l_server_name then @@ -175,7 +175,12 @@ feature -- Execution else io.error.put_string ("localhost") end - io.error.put_string (":" + port_number.out + "/" + base_url + "%N") + io.error.put_string (":" + port_number.out) + if attached base_url as b and then not b.is_empty then + io.error.put_string (b + "%N") + else + io.error.put_string ("/%N") + end end end update_configuration (conn.configuration) @@ -201,7 +206,7 @@ feature {NONE} -- Implementation server_name: detachable READABLE_STRING_8 - base_url: READABLE_STRING_8 + base_url: detachable READABLE_STRING_8 verbose: BOOLEAN verbose_level: INTEGER