diff --git a/library/network/http_network/src/http_stream_socket.e b/library/network/http_network/src/http_stream_socket.e index 3d6905c1..eab571d5 100644 --- a/library/network/http_network/src/http_stream_socket.e +++ b/library/network/http_network/src/http_stream_socket.e @@ -207,6 +207,8 @@ feature -- Output put_character_noexception (c: CHARACTER) -- Write character `c' to socket. + require + extendible: extendible do socket_buffer.put_character (c, 0) put_managed_pointer_noexception (socket_buffer, 0, character_8_bytes) @@ -217,13 +219,15 @@ feature -- Output -- No exception raised! local ext: C_STRING - do + 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. + require + extendible: extendible local ext: C_STRING do @@ -243,6 +247,27 @@ feature -- Status report Result := last_string.count = 1 end + set_bytes_sent (nb: INTEGER) + -- Set `bytes_sent` to `nb`; + do + bytes_sent := nb + end + + report_error (m: READABLE_STRING_8) + -- Report error, and set `socket_error` to `m`. + do + socket_error := m + end + +feature -- Extension + + socket: HTTP_STREAM_SOCKET + -- Associated socket. + do + Result := Current + end + + note copyright: "2011-2016, 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)" diff --git a/library/network/http_network/src/http_stream_socket_ext.e b/library/network/http_network/src/http_stream_socket_ext.e index 24331793..0555898c 100644 --- a/library/network/http_network/src/http_stream_socket_ext.e +++ b/library/network/http_network/src/http_stream_socket_ext.e @@ -8,6 +8,75 @@ note deferred class HTTP_STREAM_SOCKET_EXT -feature {NONE} -- No-Exception network operation +feature -- Extension + socket: HTTP_STREAM_SOCKET + -- Associated socket. + -- Used to extend HTTP_STREAM_SOCKET with Current interface. + deferred + end + +feature -- No-Exception network operation + + put_file_content_with_timeout (a_file: FILE; a_offset: INTEGER; a_byte_count: INTEGER; a_timeout: INTEGER) + -- Send `a_count' bytes from the content of file `a_file' starting at offset `a_offset'. + -- When relevant, use the `a_timeout` to wait on completion (if a_timeout < 0, ignored) + require + a_file_closed_or_openread: a_file.exists and then (a_file.is_access_readable or a_file.is_closed) + byte_count_positive: a_byte_count > 0 + extendible: socket.extendible + local + l_close_needed: BOOLEAN + l_bytes_sent: INTEGER + cs: C_STRING + do + if a_file.exists and then a_file.is_access_readable then + if a_file.is_open_read then + l_close_needed := False + else + l_close_needed := True + a_file.open_read + end + create cs.make (a_file.path.name) + l_bytes_sent := c_sendfile (socket.descriptor, a_file.file_pointer, a_offset, a_byte_count, a_timeout) + if l_bytes_sent < 0 then + socket.report_error ("Network error: sendfile") + end + socket.set_bytes_sent (l_bytes_sent) + if l_close_needed then + a_file.close + end + end + ensure + bytes_sent_updated: not socket.was_error implies (0 <= socket.bytes_sent and socket.bytes_sent <= a_byte_count) + end + + put_file_content (a_file: FILE; a_offset: INTEGER; a_byte_count: INTEGER) + -- Send `a_count' bytes from the content of file `a_file' starting at offset `a_offset'. + -- When relevant, use the `a_timeout` to wait on completion (if a_timeout < 0, ignored) + do + put_file_content_with_timeout (a_file, a_offset, a_byte_count, -1) + end + +feature {NONE} -- External + + c_sendfile (a_fd: INTEGER; fp: POINTER; a_offset: INTEGER; len: INTEGER; a_timeout: INTEGER): INTEGER + -- External routine to write file content from `fp` of + -- length `len' to socket `a_fd' + -- note: On Windows, due to asynchronous potential behavior, wait for completion using `timeout` value. + -- EIF_INTEGER c_sendfile_noexception(EIF_INTEGER out_fd, FILE* f, EIF_INTEGER offset, EIF_INTEGER length, EIF_INTEGER timeout). + external + "C blocking" + end + +note + copyright: "2011-2016, 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 + 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/network/http_network/src/until_16_05/http_stream_socket_ext.e b/library/network/http_network/src/until_16_05/http_stream_socket_ext.e index bb80fc59..a81fb4fc 100644 --- a/library/network/http_network/src/until_16_05/http_stream_socket_ext.e +++ b/library/network/http_network/src/until_16_05/http_stream_socket_ext.e @@ -26,28 +26,22 @@ feature -- Initialization valid_port: a_port >= 0 do make - set_address (create {like address_type}.make_from_address_and_port (a_address, a_port)) - bind + socket.set_address (create {like socket.address_type}.make_from_address_and_port (a_address, a_port)) + socket.bind end -feature -- Basic operation +feature -- Extension - bind + socket: HTTP_STREAM_SOCKET + -- Associated socket. + -- Used to extend HTTP_STREAM_SOCKET with Current interface. deferred end -feature -- Access +feature {NONE} -- Externals: socket option levels - set_address (addr: detachable like address_type) - deferred - end - - address_type: NETWORK_SOCKET_ADDRESS - deferred - end - - descriptor: INTEGER - -- Socket descriptor of current socket + level_sol_socket: INTEGER_32 + -- SOL_SOCKET level of options deferred end @@ -59,7 +53,7 @@ feature -- Socket Recv and Send timeout. require positive_timeout: a_timeout_seconds >= 0 do - c_set_sock_recv_timeout (descriptor, level_sol_socket, a_timeout_seconds) + c_set_sock_recv_timeout (socket.descriptor, level_sol_socket, a_timeout_seconds) end set_send_timeout (a_timeout_seconds: INTEGER) @@ -68,16 +62,77 @@ feature -- Socket Recv and Send timeout. require positive_timeout: a_timeout_seconds >= 0 do - c_set_sock_send_timeout (descriptor, level_sol_socket, a_timeout_seconds) + c_set_sock_send_timeout (socket.descriptor, level_sol_socket, a_timeout_seconds) + end + +feature -- Output + + put_file_content_with_timeout (a_file: FILE; a_offset: INTEGER; a_byte_count: INTEGER; a_timeout_ms: INTEGER) + -- Send `a_count' bytes from the content of file `a_file' starting at offset `a_offset'. + -- When relevant, use the `a_timeout_ms` to wait on completion (if a_timeout < 0, ignored) + require + a_file_closed_or_openread: a_file.exists and then (a_file.is_access_readable or a_file.is_closed) + byte_count_positive: a_byte_count > 0 + extendible: socket.extendible + local + l_close_needed: BOOLEAN + l_remain: INTEGER + l_done: BOOLEAN + s: STRING + l_bytes_sent: INTEGER + do + if a_file.exists and then a_file.is_access_readable then + if a_file.is_open_read then + l_close_needed := False + else + l_close_needed := True + a_file.open_read + end + if a_offset > 0 then + a_file.move (a_offset) + end + from + l_remain := a_byte_count + l_done := False + until + a_file.exhausted or l_done + loop + a_file.read_stream (l_remain.min (4_096)) + s := a_file.last_string + if s.is_empty then + -- File error? + l_done := True + else + socket.put_string_8_noexception (s) + l_bytes_sent := l_bytes_sent + socket.bytes_sent + l_remain := l_remain - s.count + check l_remain >= 0 end + l_done := l_remain = 0 + end + end + socket.set_bytes_sent (l_bytes_sent) + if l_close_needed then + a_file.close + end + end + ensure + bytes_sent_updated: not socket.was_error implies (0 <= socket.bytes_sent and socket.bytes_sent <= a_byte_count) + end + + put_file_content (a_file: FILE; a_offset: INTEGER; a_byte_count: INTEGER) + -- Send `a_count' bytes from the content of file `a_file' starting at offset `a_offset'. + require + a_file_closed_or_openread: a_file.exists and then (a_file.is_access_readable or a_file.is_closed) + byte_count_positive: a_byte_count > 0 + extendible: socket.extendible + do + put_file_content_with_timeout (a_file, a_offset, a_byte_count, -1) + ensure + bytes_sent_updated: not socket.was_error implies (0 <= socket.bytes_sent and socket.bytes_sent <= a_byte_count) 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 @@ -157,4 +212,14 @@ feature {NONE} -- No-Exception network operation ]" end +note + copyright: "2011-2016, 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 + 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/src/wgi_standalone_output_stream.e b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_output_stream.e index 1a5f709f..0ec458aa 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 @@ -12,6 +12,9 @@ class inherit WGI_OUTPUT_STREAM + redefine + put_file_content + end HTTP_STATUS_CODE_MESSAGES export @@ -94,6 +97,15 @@ feature -- Output last_target_call_succeed := not target.was_error end + put_file_content (a_file: FILE; a_offset: INTEGER; a_byte_count: INTEGER) + -- Send `a_byte_count' bytes from the content of file `a_file' starting at offset `a_offset'. + --| Could be redefine for optimization. + do + last_target_call_succeed := False + target.put_file_content (a_file, a_offset, a_byte_count) + last_target_call_succeed := not target.was_error + end + feature -- Status report is_available: BOOLEAN