mirror of
https://github.com/pocoproject/poco.git
synced 2025-02-15 10:55:25 +01:00
fix(Net): fix SocketImpl::sendFile() #4831
This commit is contained in:
parent
ca969f9ec3
commit
13029fb524
@ -287,6 +287,22 @@ public:
|
||||
/// The preferred way for a socket to receive urgent data
|
||||
/// is by enabling the SO_OOBINLINE option.
|
||||
|
||||
virtual std::streamsize sendFile(Poco::FileInputStream& FileInputStream, std::streamoff offset = 0);
|
||||
/// Sends the contents of a file in an optimized way, if possible.
|
||||
///
|
||||
/// On POSIX systems, this means using sendfile() or sendfile64().
|
||||
/// On Windows, this means using 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().
|
||||
///
|
||||
/// Returns the number of bytes sent, which may be
|
||||
/// less than the number of bytes specified.
|
||||
///
|
||||
/// Throws NetException (or a subclass) in case of any errors.
|
||||
/// Also throws if the socket is non-blocking.
|
||||
|
||||
virtual int available();
|
||||
/// Returns the number of bytes available that can be read
|
||||
/// without causing the socket to block.
|
||||
@ -487,17 +503,7 @@ public:
|
||||
|
||||
bool initialized() const;
|
||||
/// Returns true iff the underlying socket is initialized.
|
||||
#ifdef POCO_HAVE_SENDFILE
|
||||
Int64 sendFile(FileInputStream &FileInputStream, UInt64 offset = 0);
|
||||
/// Sends file using system function
|
||||
/// for posix systems - with sendfile[64](...)
|
||||
/// for windows - with TransmitFile(...)
|
||||
///
|
||||
/// Returns the number of bytes sent, which may be
|
||||
/// less than the number of bytes specified.
|
||||
///
|
||||
/// Throws NetException (or a subclass) in case of any errors.
|
||||
#endif
|
||||
|
||||
protected:
|
||||
SocketImpl();
|
||||
/// Creates a SocketImpl.
|
||||
@ -538,6 +544,14 @@ protected:
|
||||
|
||||
void checkBrokenTimeout(SelectMode mode);
|
||||
|
||||
std::streamsize sendFileNative(Poco::FileInputStream& FileInputStream, std::streamoff offset);
|
||||
/// Implements sendFile() using an OS-specific API like
|
||||
/// sendfile() or TransmitFile().
|
||||
|
||||
std::streamsize sendFileBlockwise(Poco::FileInputStream& FileInputStream, std::streamoff offset);
|
||||
/// Implements sendFile() by reading the file blockwise and
|
||||
/// calling sendBytes() for each block.
|
||||
|
||||
static int lastError();
|
||||
/// Returns the last error code.
|
||||
|
||||
|
@ -267,17 +267,23 @@ public:
|
||||
///
|
||||
/// The preferred way for a socket to receive urgent data
|
||||
/// is by enabling the SO_OOBINLINE option.
|
||||
#ifdef POCO_HAVE_SENDFILE
|
||||
IntPtr sendFile(FileInputStream &FileInputStream, UIntPtr offset = 0);
|
||||
/// Sends file using system function
|
||||
/// for posix systems - with sendfile[64](...)
|
||||
/// for windows - with TransmitFile(...)
|
||||
|
||||
std::streamsize sendFile(Poco::FileInputStream& FileInputStream, std::streamoff offset = 0);
|
||||
/// Sends the contents of a file in an optimized way, if possible.
|
||||
///
|
||||
/// On POSIX systems, this means using sendfile() or sendfile64().
|
||||
/// On Windows, this means using 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().
|
||||
///
|
||||
/// Returns the number of bytes sent, which may be
|
||||
/// less than the number of bytes specified.
|
||||
///
|
||||
/// Throws NetException (or a subclass) in case of any errors.
|
||||
#endif
|
||||
/// Also throws if the socket is non-blocking.
|
||||
|
||||
StreamSocket(SocketImpl* pImpl);
|
||||
/// Creates the Socket and attaches the given SocketImpl.
|
||||
/// The socket takes ownership of the SocketImpl.
|
||||
|
@ -135,8 +135,8 @@ void HTTPServerResponseImpl::sendFile(const std::string& path, const std::string
|
||||
#ifdef POCO_HAVE_SENDFILE
|
||||
_pStream->flush(); // flush the HTTP headers to the socket, required by HTTP 1.0 and above
|
||||
|
||||
Poco::IntPtr sent = 0;
|
||||
Poco::IntPtr offset = 0;
|
||||
std::streamsize sent = 0;
|
||||
std::streamoff offset = 0;
|
||||
while (sent < length)
|
||||
{
|
||||
offset = sent;
|
||||
|
@ -696,6 +696,27 @@ void SocketImpl::sendUrgent(unsigned char data)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::streamsize SocketImpl::sendFile(FileInputStream& fileInputStream, std::streamoff offset)
|
||||
{
|
||||
if (!getBlocking()) throw NetException("sendFile() not supported for non-blocking sockets");
|
||||
|
||||
#ifdef POCO_HAVE_SENDFILE
|
||||
if (secure())
|
||||
{
|
||||
return sendFileBlockwise(fileInputStream, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
return sendFileNative(fileInputStream, offset);
|
||||
}
|
||||
#else
|
||||
return sendFileBlockwise(fileInputStream, offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int SocketImpl::available()
|
||||
{
|
||||
int result = 0;
|
||||
@ -1424,9 +1445,13 @@ void SocketImpl::error(int code, const std::string& arg)
|
||||
throw IOException(NumberFormatter::format(code), arg, code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef POCO_HAVE_SENDFILE
|
||||
#ifdef POCO_OS_FAMILY_WINDOWS
|
||||
Poco::Int64 SocketImpl::sendFile(FileInputStream &fileInputStream, Poco::UInt64 offset)
|
||||
|
||||
|
||||
std::streamsize SocketImpl::sendFileNative(FileInputStream& fileInputStream, std::streamoff offset)
|
||||
{
|
||||
FileIOS::NativeHandle fd = fileInputStream.nativeHandle();
|
||||
UInt64 fileSize = fileInputStream.size();
|
||||
@ -1441,23 +1466,30 @@ Poco::Int64 SocketImpl::sendFile(FileInputStream &fileInputStream, Poco::UInt64
|
||||
if (overlapped.hEvent == nullptr)
|
||||
{
|
||||
int err = GetLastError();
|
||||
error(err, std::string("[sendfile error]") + Error::getMessage(err));
|
||||
error(err);
|
||||
}
|
||||
bool result = TransmitFile(_sockfd, fd, sentSize, 0, &overlapped, nullptr, 0);
|
||||
if (!result)
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
if ((err != ERROR_IO_PENDING) && (WSAGetLastError() != WSA_IO_PENDING)) {
|
||||
if ((err != ERROR_IO_PENDING) && (WSAGetLastError() != WSA_IO_PENDING))
|
||||
{
|
||||
CloseHandle(overlapped.hEvent);
|
||||
error(err, std::string("[sendfile error]") + Error::getMessage(err));
|
||||
error(err);
|
||||
}
|
||||
WaitForSingleObject(overlapped.hEvent, INFINITE);
|
||||
}
|
||||
CloseHandle(overlapped.hEvent);
|
||||
return sentSize;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
Int64 _sendfile(poco_socket_t sd, FileIOS::NativeHandle fd, UInt64 offset, std::streamoff sentSize)
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
std::streamsize sendFilePosix(poco_socket_t sd, FileIOS::NativeHandle fd, std::streamsize offset, std::streamoff sentSize)
|
||||
{
|
||||
Int64 sent = 0;
|
||||
#ifdef __USE_LARGEFILE64
|
||||
@ -1485,8 +1517,10 @@ Int64 _sendfile(poco_socket_t sd, FileIOS::NativeHandle fd, UInt64 offset, std::
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
}
|
||||
|
||||
Int64 SocketImpl::sendFile(FileInputStream &fileInputStream, UInt64 offset)
|
||||
|
||||
std::streamsize SocketImpl::sendFileNative(FileInputStream& fileInputStream, std::streamoff offset)
|
||||
{
|
||||
FileIOS::NativeHandle fd = fileInputStream.nativeHandle();
|
||||
UInt64 fileSize = fileInputStream.size();
|
||||
@ -1500,10 +1534,10 @@ Int64 SocketImpl::sendFile(FileInputStream &fileInputStream, UInt64 offset)
|
||||
while (sent == 0)
|
||||
{
|
||||
errno = 0;
|
||||
sent = _sendfile(_sockfd, fd, offset, sentSize);
|
||||
sent = sendFilePosix(_sockfd, fd, offset, sentSize);
|
||||
if (sent < 0)
|
||||
{
|
||||
error(errno, std::string("[sendfile error]") + Error::getMessage(errno));
|
||||
error(errno);
|
||||
}
|
||||
}
|
||||
if (old_sa.sa_handler == SIG_ERR)
|
||||
@ -1513,7 +1547,33 @@ Int64 SocketImpl::sendFile(FileInputStream &fileInputStream, UInt64 offset)
|
||||
sigaction(SIGPIPE, &old_sa, nullptr);
|
||||
return sent;
|
||||
}
|
||||
|
||||
|
||||
#endif // POCO_OS_FAMILY_WINDOWS
|
||||
#endif // POCO_HAVE_SENDFILE
|
||||
|
||||
|
||||
std::streamsize SocketImpl::sendFileBlockwise(FileInputStream& fileInputStream, std::streamoff offset)
|
||||
{
|
||||
fileInputStream.seekg(offset);
|
||||
Poco::Buffer<char> buffer(8192);
|
||||
|
||||
std::streamsize len = 0;
|
||||
fileInputStream.read(buffer.begin(), buffer.size());
|
||||
std::streamsize n = fileInputStream.gcount();
|
||||
while (n > 0)
|
||||
{
|
||||
len += n;
|
||||
sendBytes(buffer.begin(), n);
|
||||
if (fileInputStream)
|
||||
{
|
||||
fileInputStream.read(buffer.begin(), buffer.size());
|
||||
n = fileInputStream.gcount();
|
||||
}
|
||||
else n = 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::Net
|
||||
|
@ -212,10 +212,12 @@ void StreamSocket::sendUrgent(unsigned char data)
|
||||
{
|
||||
impl()->sendUrgent(data);
|
||||
}
|
||||
#ifdef POCO_HAVE_SENDFILE
|
||||
IntPtr StreamSocket::sendFile(FileInputStream &fileInputStream, UIntPtr offset)
|
||||
|
||||
|
||||
std::streamsize StreamSocket::sendFile(Poco::FileInputStream& fileInputStream, std::streamoff offset)
|
||||
{
|
||||
return impl()->sendFile(fileInputStream, offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
} } // namespace Poco::Net
|
||||
|
Loading…
x
Reference in New Issue
Block a user