Skip to content

Commit

Permalink
Using native sendfile from OS for HttpServer (#4351)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
bas524 authored Sep 1, 2024
1 parent 73df368 commit 710c2a4
Show file tree
Hide file tree
Showing 15 changed files with 145 additions and 29 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions Foundation/include/Poco/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion Foundation/include/Poco/FileStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class Foundation_API FileIOS: public virtual std::ios
NativeHandle nativeHandle() const;
/// Returns native file descriptor handle

Poco::UInt64 size() const;
UInt64 size() const;
/// Returns file size

void flushToDisk();
Expand Down
2 changes: 1 addition & 1 deletion Foundation/include/Poco/FileStream_POSIX.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class Foundation_API FileStreamBuf: public BufferedBidirectionalStreamBuf
NativeHandle nativeHandle() const;
/// Returns native file descriptor handle

Poco::UInt64 size() const;
UInt64 size() const;
/// Returns file size

protected:
Expand Down
1 change: 0 additions & 1 deletion Foundation/include/Poco/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions Net/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 8 additions & 3 deletions Net/include/Poco/Net/SocketImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -477,12 +477,17 @@ class Net_API SocketImpl: public Poco::RefCountedObject

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.
Expand Down
11 changes: 8 additions & 3 deletions Net/include/Poco/Net/StreamSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,17 @@ class Net_API StreamSocket: public Socket
///
/// 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.
Expand Down
15 changes: 15 additions & 0 deletions Net/src/HTTPServerResponseImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -37,6 +39,7 @@ using Poco::StreamCopier;
using Poco::OpenFileException;
using Poco::DateTimeFormatter;
using Poco::DateTimeFormat;
using Poco::Error;
using namespace std::string_literals;


Expand Down Expand Up @@ -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);
Expand Down
26 changes: 16 additions & 10 deletions Net/src/SocketImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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;
Expand All @@ -1388,25 +1388,26 @@ 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)
{
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);
}
CloseHandle(overlapped.hEvent);
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
Expand All @@ -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
6 changes: 3 additions & 3 deletions Net/src/StreamSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
57 changes: 55 additions & 2 deletions Net/testsuite/src/HTTPServerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>


Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}
};
}
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions Net/testsuite/src/HTTPServerTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class HTTPServerTest: public CppUnit::TestCase
void testAuth();
void testNotImpl();
void testBuffer();
void testFile();
void testChunkedTrailer();

void setUp();
Expand Down
Loading

0 comments on commit 710c2a4

Please sign in to comment.