From f32e3d0b9082beadeb54065a96ddb3adfd4c9284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Obiltschnig?= Date: Sun, 9 Feb 2025 16:03:51 +0100 Subject: [PATCH] enh(Net): SocketImpl::sendFile() sends entire contents by repeatedly calling sendfile() if necessary --- Net/include/Poco/Net/SocketImpl.h | 22 +++++++++++++--------- Net/include/Poco/Net/StreamSocket.h | 22 +++++++++++++--------- Net/src/HTTPServerResponseImpl.cpp | 13 +------------ Net/src/SocketImpl.cpp | 19 ++++++++++--------- 4 files changed, 37 insertions(+), 39 deletions(-) diff --git a/Net/include/Poco/Net/SocketImpl.h b/Net/include/Poco/Net/SocketImpl.h index 4537f1965..f83d857c4 100644 --- a/Net/include/Poco/Net/SocketImpl.h +++ b/Net/include/Poco/Net/SocketImpl.h @@ -288,23 +288,27 @@ public: /// is by enabling the SO_OOBINLINE option. virtual std::streamsize sendFile(Poco::FileInputStream& FileInputStream, std::streamoff offset = 0, std::streamsize count = 0); - /// Sends the contents of a file in an optimized way, if possible. + /// Sends the contents of a file over the socket, using operating + /// system-specific APIs, if available. The socket must not have + /// been set to non-blocking. /// /// If count is != 0, sends the given number of bytes, otherwise /// sends all bytes, starting from the given offset. /// - /// On POSIX systems, this means using sendfile() or sendfile64(). - /// On Windows, this means using TransmitFile(). + /// On Linux, macOS and FreeBSD systems, the implementation + /// uses sendfile() or sendfile64(). + /// On Windows, the implementation uses TransmitFile(). /// - /// If neither is available, or the socket is a SecureSocketImpl() - /// (secure() returns true), falls back to reading the file - /// block by block and callind sendBytes(). + /// If neither sendfile() nor TransmitFile() is available, + /// or the socket is a secure one (secure() returne true), + /// falls back to reading the file block by block and calling sendBytes(). /// - /// Returns the number of bytes sent, which may be - /// less than the number of bytes specified. + /// Returns the number of bytes sent, which should be the same + /// as count, unless count is 0. /// /// Throws NetException (or a subclass) in case of any errors. - /// Also throws if the socket is non-blocking. + /// Also throws a NetException if the socket has been set to + /// non-blocking. virtual int available(); /// Returns the number of bytes available that can be read diff --git a/Net/include/Poco/Net/StreamSocket.h b/Net/include/Poco/Net/StreamSocket.h index e81c25a4d..e98405881 100644 --- a/Net/include/Poco/Net/StreamSocket.h +++ b/Net/include/Poco/Net/StreamSocket.h @@ -269,23 +269,27 @@ public: /// is by enabling the SO_OOBINLINE option. std::streamsize sendFile(Poco::FileInputStream& FileInputStream, std::streamoff offset = 0, std::streamsize count = 0); - /// Sends the contents of a file in an optimized way, if possible. + /// Sends the contents of a file over the socket, using operating + /// system-specific APIs, if available. The socket must not have + /// been set to non-blocking. /// /// If count is != 0, sends the given number of bytes, otherwise /// sends all bytes, starting from the given offset. /// - /// On POSIX systems, this means using sendfile() or sendfile64(). - /// On Windows, this means using TransmitFile(). + /// On Linux, macOS and FreeBSD systems, the implementation + /// uses sendfile() or sendfile64(). + /// On Windows, the implementation uses TransmitFile(). /// - /// If neither is available, or the socket is a SecureSocketImpl() - /// (secure() returns true), falls back to reading the file - /// block by block and callind sendBytes(). + /// If neither sendfile() nor TransmitFile() is available, + /// or the socket is a SecureStreamSocket (secure() returne true), + /// falls back to reading the file block by block and calling sendBytes(). /// - /// Returns the number of bytes sent, which may be - /// less than the number of bytes specified. + /// Returns the number of bytes sent, which should be the same + /// as count, unless count is 0. /// /// Throws NetException (or a subclass) in case of any errors. - /// Also throws if the socket is non-blocking. + /// Also throws a NetException if the socket has been set to + /// non-blocking. StreamSocket(SocketImpl* pImpl); /// Creates the Socket and attaches the given SocketImpl. diff --git a/Net/src/HTTPServerResponseImpl.cpp b/Net/src/HTTPServerResponseImpl.cpp index d1d4dc1a0..7195dd7b0 100644 --- a/Net/src/HTTPServerResponseImpl.cpp +++ b/Net/src/HTTPServerResponseImpl.cpp @@ -132,19 +132,8 @@ void HTTPServerResponseImpl::sendFile(const std::string& path, const std::string write(*_pStream); if (_pRequest && _pRequest->getMethod() != HTTPRequest::HTTP_HEAD) { -#ifdef POCO_HAVE_SENDFILE _pStream->flush(); // flush the HTTP headers to the socket, required by HTTP 1.0 and above - - std::streamsize sent = 0; - std::streamoff offset = 0; - while (sent < length) - { - offset = sent; - sent += _session.socket().sendFile(istr, offset); - } -#else - StreamCopier::copyStream(istr, *_pStream); -#endif + _session.socket().sendFile(istr); } } else throw OpenFileException(path); diff --git a/Net/src/SocketImpl.cpp b/Net/src/SocketImpl.cpp index 706c14aa7..e1fb34b27 100644 --- a/Net/src/SocketImpl.cpp +++ b/Net/src/SocketImpl.cpp @@ -1521,10 +1521,6 @@ namespace throw Poco::NotImplementedException("native sendfile not implemented for this platform"); #endif #endif - if (errno == EAGAIN || errno == EWOULDBLOCK) - { - sent = 0; - } return sent; } } @@ -1534,12 +1530,17 @@ std::streamsize SocketImpl::sendFileNative(FileInputStream& fileInputStream, std { FileIOS::NativeHandle fd = fileInputStream.nativeHandle(); if (count == 0) count = fileInputStream.size() - offset; - std::streamoff sent = 0; - while (sent == 0) + std::streamsize sent = 0; + while (count > 0) { - errno = 0; - sent = sendFileUnix(_sockfd, fd, offset, count); - if (sent < 0) + std::streamoff rc = sendFileUnix(_sockfd, fd, offset, count); + if (rc >= 0) + { + sent += rc; + offset += rc; + count -= rc; + } + else { error(errno); }