mirror of
https://github.com/pocoproject/poco.git
synced 2025-03-27 08:46:17 +01:00
Using native sendfile from OS for HttpServer (#4351)
* add possibility to use native sendFile from OS for HttpServerResponse add option POCO_USE_SENDFILE_FOR_HTTPSERVER, default - OFF add test for HttpServer - testFile add define fro Config.h POCO_USE_SENDFILE_FOR_HTTPSERVER * my fail, be carefull with macro and brackets * replace option POCO_USE_SENDFILE_FOR_HTTPSERVER with compiletime detected macro POCO_HAVE_SENDFILE replace types for sendFile with platform depended wrap possibility of using sendFile with macro, if sendFile doesn't exist in OS, then all methods don't exist * remove option POCO_USE_SENDFILE_FOR_HTTPSERVER from ci.yml * wrap testSendFile in the suite with define POCO_HAVE_SENDFILE * try fix compile problem with emscripten * oh, emscripten again * fix logical error in testSendFile * fix problem with cmake-specific macro when usinf make-project * revert types from platform depended to Poco::Int64 and Poco::UInt64 for sendfile
This commit is contained in:
parent
73df3689bf
commit
710c2a41f3
@ -249,6 +249,7 @@ option(POCO_ENABLE_STD_MUTEX "Set to OFF|NO using mutex from standard library (d
|
||||
if (POCO_ENABLE_STD_MUTEX)
|
||||
add_definitions(-DPOCO_ENABLE_STD_MUTEX)
|
||||
endif ()
|
||||
|
||||
include(DefinePlatformSpecifc)
|
||||
|
||||
# Collect the built libraries and include dirs, the will be used to create the PocoConfig.cmake file
|
||||
|
@ -181,6 +181,10 @@
|
||||
// #define POCO_ENABLE_STD_MUTEX
|
||||
#endif
|
||||
|
||||
#ifndef POCO_HAVE_SENDFILE
|
||||
// #define POCO_HAVE_SENDFILE
|
||||
#endif
|
||||
|
||||
#define POCO_HAVE_CPP17_COMPILER (__cplusplus >= 201703L)
|
||||
|
||||
// Option to silence deprecation warnings.
|
||||
|
@ -79,7 +79,7 @@ public:
|
||||
NativeHandle nativeHandle() const;
|
||||
/// Returns native file descriptor handle
|
||||
|
||||
Poco::UInt64 size() const;
|
||||
UInt64 size() const;
|
||||
/// Returns file size
|
||||
|
||||
void flushToDisk();
|
||||
|
@ -61,7 +61,7 @@ public:
|
||||
NativeHandle nativeHandle() const;
|
||||
/// Returns native file descriptor handle
|
||||
|
||||
Poco::UInt64 size() const;
|
||||
UInt64 size() const;
|
||||
/// Returns file size
|
||||
|
||||
protected:
|
||||
|
@ -47,7 +47,6 @@ using UInt64 = std::uint64_t;
|
||||
using IntPtr = std::intptr_t;
|
||||
using UIntPtr = std::uintptr_t;
|
||||
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_WIN64)
|
||||
#define POCO_PTR_IS_64_BIT 1
|
||||
|
@ -10,6 +10,28 @@ POCO_HEADERS_AUTO(SRCS ${HDRS_G})
|
||||
POCO_SOURCES_AUTO_PLAT(SRCS WIN32 src/wepoll.c)
|
||||
POCO_HEADERS_AUTO(SRCS src/wepoll.h)
|
||||
|
||||
if (MSVC)
|
||||
set(HAVE_SENDFILE ON)
|
||||
else()
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckSymbolExists)
|
||||
check_include_files(sys/sendfile.h HAVE_SYS_SENDFILE_H)
|
||||
if(HAVE_SYS_SENDFILE_H)
|
||||
check_symbol_exists(sendfile sys/sendfile.h HAVE_SENDFILE)
|
||||
if (NOT DEFINED HAVE_SENDFILE)
|
||||
check_symbol_exists(sendfile64 sys/sendfile.h HAVE_SENDFILE)
|
||||
endif()
|
||||
else()
|
||||
# BSD version
|
||||
check_symbol_exists(sendfile "sys/types.h;sys/socket.h;sys/uio.h" HAVE_SENDFILE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (DEFINED HAVE_SENDFILE)
|
||||
message(STATUS "OS has native sendfile function")
|
||||
add_definitions(-DPOCO_HAVE_SENDFILE)
|
||||
endif()
|
||||
|
||||
# Version Resource
|
||||
if(MSVC AND BUILD_SHARED_LIBS)
|
||||
source_group("Resources" FILES ${PROJECT_SOURCE_DIR}/DLLVersion.rc)
|
||||
|
@ -477,12 +477,17 @@ public:
|
||||
|
||||
bool initialized() const;
|
||||
/// Returns true iff the underlying socket is initialized.
|
||||
|
||||
Poco::Int64 sendFile(FileInputStream &FileInputStream, Poco::UInt64 offset = 0);
|
||||
#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.
|
||||
|
@ -257,12 +257,17 @@ public:
|
||||
///
|
||||
/// The preferred way for a socket to receive urgent data
|
||||
/// is by enabling the SO_OOBINLINE option.
|
||||
|
||||
Poco::Int64 sendFile(FileInputStream &FileInputStream, Poco::UInt64 offset = 0);
|
||||
#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(...)
|
||||
|
||||
///
|
||||
/// 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
|
||||
StreamSocket(SocketImpl* pImpl);
|
||||
/// Creates the Socket and attaches the given SocketImpl.
|
||||
/// The socket takes ownership of the SocketImpl.
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include "Poco/FileStream.h"
|
||||
#include "Poco/DateTimeFormatter.h"
|
||||
#include "Poco/DateTimeFormat.h"
|
||||
#include "Poco/Error.h"
|
||||
#include "Poco/Net/NetException.h"
|
||||
|
||||
|
||||
using Poco::File;
|
||||
@ -37,6 +39,7 @@ using Poco::StreamCopier;
|
||||
using Poco::OpenFileException;
|
||||
using Poco::DateTimeFormatter;
|
||||
using Poco::DateTimeFormat;
|
||||
using Poco::Error;
|
||||
using namespace std::string_literals;
|
||||
|
||||
|
||||
@ -129,7 +132,19 @@ 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
|
||||
|
||||
Poco::IntPtr sent = 0;
|
||||
Poco::IntPtr offset = 0;
|
||||
while (sent < length)
|
||||
{
|
||||
offset = sent;
|
||||
sent += _session.socket().sendFile(istr, offset);
|
||||
}
|
||||
#else
|
||||
StreamCopier::copyStream(istr, *_pStream);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else throw OpenFileException(path);
|
||||
|
@ -56,7 +56,7 @@
|
||||
using sighandler_t = sig_t;
|
||||
#endif
|
||||
|
||||
#if POCO_OS == POCO_OS_LINUX && !defined(POCO_EMSCRIPTEN)
|
||||
#if POCO_OS == POCO_OS_LINUX && defined(POCO_HAVE_SENDFILE) && !defined(POCO_EMSCRIPTEN)
|
||||
#include <sys/sendfile.h>
|
||||
#endif
|
||||
|
||||
@ -1372,12 +1372,12 @@ 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)
|
||||
{
|
||||
FileIOS::NativeHandle fd = fileInputStream.nativeHandle();
|
||||
Poco::UInt64 fileSize = fileInputStream.size();
|
||||
UInt64 fileSize = fileInputStream.size();
|
||||
std::streamoff sentSize = fileSize - offset;
|
||||
LARGE_INTEGER offsetHelper;
|
||||
offsetHelper.QuadPart = offset;
|
||||
@ -1388,7 +1388,8 @@ Poco::Int64 SocketImpl::sendFile(FileInputStream &fileInputStream, Poco::UInt64
|
||||
overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (overlapped.hEvent == nullptr)
|
||||
{
|
||||
return -1;
|
||||
int err = GetLastError();
|
||||
error(err, std::string("[sendfile error]") + Error::getMessage(err));
|
||||
}
|
||||
bool result = TransmitFile(_sockfd, fd, sentSize, 0, &overlapped, nullptr, 0);
|
||||
if (!result)
|
||||
@ -1396,7 +1397,7 @@ Poco::Int64 SocketImpl::sendFile(FileInputStream &fileInputStream, Poco::UInt64
|
||||
int err = WSAGetLastError();
|
||||
if ((err != ERROR_IO_PENDING) && (WSAGetLastError() != WSA_IO_PENDING)) {
|
||||
CloseHandle(overlapped.hEvent);
|
||||
error(err, Error::getMessage(err));
|
||||
error(err, std::string("[sendfile error]") + Error::getMessage(err));
|
||||
}
|
||||
WaitForSingleObject(overlapped.hEvent, INFINITE);
|
||||
}
|
||||
@ -1404,9 +1405,9 @@ Poco::Int64 SocketImpl::sendFile(FileInputStream &fileInputStream, Poco::UInt64
|
||||
return sentSize;
|
||||
}
|
||||
#else
|
||||
Poco::Int64 _sendfile(poco_socket_t sd, FileIOS::NativeHandle fd, Poco::UInt64 offset,std::streamoff sentSize)
|
||||
Int64 _sendfile(poco_socket_t sd, FileIOS::NativeHandle fd, UInt64 offset, std::streamoff sentSize)
|
||||
{
|
||||
Poco::Int64 sent = 0;
|
||||
Int64 sent = 0;
|
||||
#ifdef __USE_LARGEFILE64
|
||||
sent = sendfile64(sd, fd, (off64_t *)&offset, sentSize);
|
||||
#else
|
||||
@ -1433,21 +1434,26 @@ Poco::Int64 _sendfile(poco_socket_t sd, FileIOS::NativeHandle fd, Poco::UInt64 o
|
||||
return sent;
|
||||
}
|
||||
|
||||
Poco::Int64 SocketImpl::sendFile(FileInputStream &fileInputStream, Poco::UInt64 offset)
|
||||
Int64 SocketImpl::sendFile(FileInputStream &fileInputStream, UInt64 offset)
|
||||
{
|
||||
FileIOS::NativeHandle fd = fileInputStream.nativeHandle();
|
||||
Poco::UInt64 fileSize = fileInputStream.size();
|
||||
UInt64 fileSize = fileInputStream.size();
|
||||
std::streamoff sentSize = fileSize - offset;
|
||||
Poco::Int64 sent = 0;
|
||||
Int64 sent = 0;
|
||||
sighandler_t sigPrev = signal(SIGPIPE, SIG_IGN);
|
||||
while (sent == 0)
|
||||
{
|
||||
errno = 0;
|
||||
sent = _sendfile(_sockfd, fd, offset, sentSize);
|
||||
if (sent < 0)
|
||||
{
|
||||
error(errno, std::string("[sendfile error]") + Error::getMessage(errno));
|
||||
}
|
||||
}
|
||||
signal(SIGPIPE, sigPrev != SIG_ERR ? sigPrev : SIG_DFL);
|
||||
return sent;
|
||||
}
|
||||
#endif // POCO_OS_FAMILY_WINDOWS
|
||||
#endif // POCO_HAVE_SENDFILE
|
||||
|
||||
} } // namespace Poco::Net
|
||||
|
@ -212,10 +212,10 @@ void StreamSocket::sendUrgent(unsigned char data)
|
||||
{
|
||||
impl()->sendUrgent(data);
|
||||
}
|
||||
|
||||
Poco::Int64 StreamSocket::sendFile(FileInputStream &fileInputStream, Poco::UInt64 offset)
|
||||
#ifdef POCO_HAVE_SENDFILE
|
||||
IntPtr StreamSocket::sendFile(FileInputStream &fileInputStream, UIntPtr offset)
|
||||
{
|
||||
return impl()->sendFile(fileInputStream, offset);
|
||||
}
|
||||
|
||||
#endif
|
||||
} } // namespace Poco::Net
|
||||
|
@ -24,6 +24,9 @@
|
||||
#include "Poco/Net/HTTPServerSession.h"
|
||||
#include "Poco/Net/ServerSocket.h"
|
||||
#include "Poco/StreamCopier.h"
|
||||
#include "Poco/Path.h"
|
||||
#include "Poco/FileStream.h"
|
||||
#include "Poco/File.h"
|
||||
#include <sstream>
|
||||
|
||||
|
||||
@ -40,10 +43,15 @@ using Poco::Net::HTTPServerResponse;
|
||||
using Poco::Net::HTTPMessage;
|
||||
using Poco::Net::ServerSocket;
|
||||
using Poco::StreamCopier;
|
||||
using Poco::Path;
|
||||
using Poco::File;
|
||||
using Poco::FileOutputStream;
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
static const int sendFileSize = 64000;
|
||||
|
||||
class EchoBodyRequestHandler: public HTTPRequestHandler
|
||||
{
|
||||
public:
|
||||
@ -105,7 +113,29 @@ namespace
|
||||
response.sendBuffer(data.data(), data.length());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class FileRequestHandler: public HTTPRequestHandler
|
||||
{
|
||||
public:
|
||||
void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response)
|
||||
{
|
||||
std::string payload(sendFileSize, 'x');
|
||||
Poco::Path testFilePath = Poco::Path::temp().append("test.http.server.sendfile.txt");
|
||||
const std::string fileName = testFilePath.toString();
|
||||
{
|
||||
File f(fileName);
|
||||
if (f.exists())
|
||||
{
|
||||
f.remove();
|
||||
}
|
||||
}
|
||||
FileOutputStream fout(fileName);
|
||||
fout << payload;
|
||||
fout.close();
|
||||
response.sendFile(fileName, "text/plain");
|
||||
}
|
||||
};
|
||||
|
||||
class TrailerRequestHandler: public HTTPRequestHandler
|
||||
{
|
||||
public:
|
||||
@ -138,8 +168,10 @@ namespace
|
||||
return new BufferRequestHandler;
|
||||
else if (request.getURI() == "/trailer")
|
||||
return new TrailerRequestHandler;
|
||||
else if (request.getURI() == "/file")
|
||||
return new FileRequestHandler;
|
||||
else
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -534,6 +566,26 @@ void HTTPServerTest::testBuffer()
|
||||
assertTrue (rbody == "xxxxxxxxxx");
|
||||
}
|
||||
|
||||
void HTTPServerTest::testFile()
|
||||
{
|
||||
std::string payload(sendFileSize, 'x');
|
||||
|
||||
ServerSocket svs(0);
|
||||
HTTPServerParams* pParams = new HTTPServerParams;
|
||||
pParams->setKeepAlive(false);
|
||||
HTTPServer srv(new RequestHandlerFactory, svs, pParams);
|
||||
srv.start();
|
||||
|
||||
HTTPClientSession cs("127.0.0.1", svs.address().port());
|
||||
HTTPRequest request("GET", "/file");
|
||||
cs.sendRequest(request);
|
||||
HTTPResponse response;
|
||||
std::string rbody;
|
||||
cs.receiveResponse(response) >> rbody;
|
||||
assertTrue (response.getStatus() == HTTPResponse::HTTP_OK);
|
||||
assertTrue (rbody == payload);
|
||||
}
|
||||
|
||||
|
||||
void HTTPServerTest::testChunkedTrailer()
|
||||
{
|
||||
@ -585,6 +637,7 @@ CppUnit::Test* HTTPServerTest::suite()
|
||||
CppUnit_addTest(pSuite, HTTPServerTest, testAuth);
|
||||
CppUnit_addTest(pSuite, HTTPServerTest, testNotImpl);
|
||||
CppUnit_addTest(pSuite, HTTPServerTest, testBuffer);
|
||||
CppUnit_addTest(pSuite, HTTPServerTest, testFile);
|
||||
CppUnit_addTest(pSuite, HTTPServerTest, testChunkedTrailer);
|
||||
|
||||
return pSuite;
|
||||
|
@ -38,6 +38,7 @@ public:
|
||||
void testAuth();
|
||||
void testNotImpl();
|
||||
void testBuffer();
|
||||
void testFile();
|
||||
void testChunkedTrailer();
|
||||
|
||||
void setUp();
|
||||
|
@ -124,6 +124,7 @@ void SocketStreamTest::testEOF()
|
||||
ss.close();
|
||||
}
|
||||
|
||||
#ifdef POCO_HAVE_SENDFILE
|
||||
void SocketStreamTest::testSendFile()
|
||||
{
|
||||
const int fileSize = 64000;
|
||||
@ -147,11 +148,11 @@ void SocketStreamTest::testSendFile()
|
||||
|
||||
SocketStream str(ss);
|
||||
|
||||
Poco::UInt64 offset = 0;
|
||||
Poco::Int64 sent = 0;
|
||||
Poco::UIntPtr offset = 0;
|
||||
Poco::IntPtr sent = 0;
|
||||
try
|
||||
{
|
||||
sent = ss.sendFile(fin);
|
||||
sent = ss.sendFile(fin);
|
||||
}
|
||||
catch (Poco::NotImplementedException &)
|
||||
{
|
||||
@ -162,8 +163,7 @@ void SocketStreamTest::testSendFile()
|
||||
while (sent < fileSize)
|
||||
{
|
||||
offset = sent;
|
||||
sent = ss.sendFile(fin, offset);
|
||||
assertTrue(sent >= 0);
|
||||
sent += ss.sendFile(fin, offset);
|
||||
}
|
||||
str.flush();
|
||||
assertTrue (str.good());
|
||||
@ -180,6 +180,7 @@ void SocketStreamTest::testSendFile()
|
||||
File f(fileName);
|
||||
f.remove();
|
||||
}
|
||||
#endif
|
||||
|
||||
void SocketStreamTest::setUp()
|
||||
{
|
||||
@ -198,7 +199,9 @@ CppUnit::Test* SocketStreamTest::suite()
|
||||
CppUnit_addTest(pSuite, SocketStreamTest, testStreamEcho);
|
||||
CppUnit_addTest(pSuite, SocketStreamTest, testLargeStreamEcho);
|
||||
CppUnit_addTest(pSuite, SocketStreamTest, testEOF);
|
||||
#ifdef POCO_HAVE_SENDFILE
|
||||
CppUnit_addTest(pSuite, SocketStreamTest, testSendFile);
|
||||
#endif
|
||||
|
||||
return pSuite;
|
||||
}
|
||||
|
@ -27,7 +27,9 @@ public:
|
||||
void testStreamEcho();
|
||||
void testLargeStreamEcho();
|
||||
void testEOF();
|
||||
#ifdef POCO_HAVE_SENDFILE
|
||||
void testSendFile();
|
||||
#endif
|
||||
|
||||
void setUp();
|
||||
void tearDown();
|
||||
|
Loading…
x
Reference in New Issue
Block a user