fix(Net): Add Unix socket support on windows #4208 (#4209)

* fix(Net): Add Unix socket support on windows #4208

* feat(Net): add abstract local socket support #4208

* fix(PollSet): use localhost socket in PollSet on windows when available #4208

* fix(PollSetTest): comment checking unconnected sockets status (Linux epoll signals them as readable/writable)
This commit is contained in:
Aleksandar Fabijanic 2023-10-23 13:33:46 +02:00 committed by GitHub
parent c918c70e68
commit e40f07099d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 243 additions and 98 deletions

View File

@ -22,7 +22,8 @@
"${POCO_BASE}/JWT/include", "${POCO_BASE}/JWT/include",
"${POCO_BASE}/Redis/include", "${POCO_BASE}/Redis/include",
"${POCO_BASE}/MongoDB/include", "${POCO_BASE}/MongoDB/include",
"${POCO_BASE}/ApacheConnector/include" "${POCO_BASE}/ApacheConnector/include",
"/usr/include"
] ]
}, },
"configurations": [ "configurations": [

View File

@ -149,6 +149,10 @@
// No UNIX socket support // No UNIX socket support
// Define to disable unix sockets // Define to disable unix sockets
// UNIX local sockets are default-enabled on
// all UNIX systems, on Windows if available
// See Net/SocketDefs.h
// See https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/
// #define POCO_NET_NO_UNIX_SOCKET // #define POCO_NET_NO_UNIX_SOCKET

View File

@ -37,8 +37,11 @@ class IPAddress;
class Net_API SocketAddress class Net_API SocketAddress
/// This class represents an internet (IP) endpoint/socket /// This class represents an internet (IP) endpoint/socket
/// address. The address can belong either to the /// address. The address can belong either to the
/// IPv4 or the IPv6 address family and consists of a /// IPv4, IPv6 or Unix local family.
/// host address and a port number. /// IP addresses consist of a host address and a port number.
/// An Unix local socket address consists of a path to socket file.
/// Abstract local sockets, which operate without the need for
/// interaction with the filesystem, are supported on Linux only.
{ {
public: public:
// The following declarations keep the Family type // The following declarations keep the Family type
@ -49,7 +52,7 @@ public:
#if defined(POCO_HAVE_IPv6) #if defined(POCO_HAVE_IPv6)
static const Family IPv6 = AddressFamily::IPv6; static const Family IPv6 = AddressFamily::IPv6;
#endif #endif
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
static const Family UNIX_LOCAL = AddressFamily::UNIX_LOCAL; static const Family UNIX_LOCAL = AddressFamily::UNIX_LOCAL;
#endif #endif
@ -124,16 +127,30 @@ public:
/// [::ffff:192.168.1.120]:2040 /// [::ffff:192.168.1.120]:2040
/// www.appinf.com:8080 /// www.appinf.com:8080
/// ///
/// On POSIX platforms supporting UNIX_LOCAL sockets, hostAndPort /// On platforms supporting UNIX_LOCAL sockets, hostAndPort
/// can also be the absolute path of a local socket, starting with a /// can also be a valid absolute local socket file path.
/// slash, e.g. "/tmp/local.socket". ///
/// Examples:
/// /tmp/local.sock
/// C:\Temp\local.sock
///
/// On Linux, abstract local sockets are supported as well.
/// Abstract local sockets operate in a namespace that has
/// no need for a filesystem. They are identified by
/// a null byte at the beginning of the path.
///
/// Example:
/// \0abstract.sock
///
SocketAddress(Family family, const std::string& addr); SocketAddress(Family family, const std::string& addr);
/// Creates a SocketAddress of the given family from a /// Creates a SocketAddress of the given family from a
/// string representation of the address, which is /// string representation of the address, which is
/// either an IP address and port number, separated by /// either (1) an IP address and port number, separated by
/// a colon for IPv4 or IPv6 addresses, or a path for /// a colon for IPv4 or IPv6 addresses, or (2) path for
/// UNIX_LOCAL sockets. /// UNIX_LOCAL sockets.
/// See `SocketAddress(const string&)` documentation
/// for more details.
SocketAddress(const SocketAddress& addr); SocketAddress(const SocketAddress& addr);
/// Creates a SocketAddress by copying another one. /// Creates a SocketAddress by copying another one.
@ -181,7 +198,7 @@ public:
enum enum
{ {
MAX_ADDRESS_LENGTH = MAX_ADDRESS_LENGTH =
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
sizeof(struct sockaddr_un) sizeof(struct sockaddr_un)
#elif defined(POCO_HAVE_IPv6) #elif defined(POCO_HAVE_IPv6)
sizeof(struct sockaddr_in6) sizeof(struct sockaddr_in6)
@ -214,7 +231,7 @@ private:
void newIPv6(const IPAddress& hostAddress, Poco::UInt16 portNumber); void newIPv6(const IPAddress& hostAddress, Poco::UInt16 portNumber);
#endif #endif
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
void newLocal(const sockaddr_un* sockAddr); void newLocal(const sockaddr_un* sockAddr);
void newLocal(const std::string& path); void newLocal(const std::string& path);
#endif #endif
@ -265,7 +282,7 @@ inline void SocketAddress::newIPv6(const IPAddress& hostAddress, Poco::UInt16 po
#endif // POCO_HAVE_IPv6 #endif // POCO_HAVE_IPv6
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
inline void SocketAddress::newLocal(const sockaddr_un* sockAddr) inline void SocketAddress::newLocal(const sockaddr_un* sockAddr)
{ {
_pImpl = new Poco::Net::Impl::LocalSocketAddressImpl(sockAddr); _pImpl = new Poco::Net::Impl::LocalSocketAddressImpl(sockAddr);
@ -276,12 +293,12 @@ inline void SocketAddress::newLocal(const std::string& path)
{ {
_pImpl = new Poco::Net::Impl::LocalSocketAddressImpl(path.c_str(), path.size()); _pImpl = new Poco::Net::Impl::LocalSocketAddressImpl(path.c_str(), path.size());
} }
#endif // POCO_OS_FAMILY_UNIX #endif // POCO_HAS_UNIX_SOCKET
inline bool SocketAddress::operator == (const SocketAddress& socketAddress) const inline bool SocketAddress::operator == (const SocketAddress& socketAddress) const
{ {
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
if (family() == UNIX_LOCAL) if (family() == UNIX_LOCAL)
return toString() == socketAddress.toString(); return toString() == socketAddress.toString();
else else

View File

@ -177,7 +177,7 @@ inline SocketAddressImpl::Family IPv6SocketAddressImpl::family() const
#endif // POCO_HAVE_IPv6 #endif // POCO_HAVE_IPv6
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
class Net_API LocalSocketAddressImpl: public SocketAddressImpl class Net_API LocalSocketAddressImpl: public SocketAddressImpl

View File

@ -30,6 +30,14 @@
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <ws2def.h> #include <ws2def.h>
#if !defined (POCO_NET_NO_UNIX_SOCKET)
#if (__cplusplus >= 201703L)
#if __has_include(<afunix.h>)
#include <afunix.h>
#define POCO_HAS_UNIX_SOCKET
#endif
#endif
#endif
#define POCO_INVALID_SOCKET INVALID_SOCKET #define POCO_INVALID_SOCKET INVALID_SOCKET
#define poco_socket_t SOCKET #define poco_socket_t SOCKET
#define poco_socklen_t int #define poco_socklen_t int
@ -148,7 +156,6 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <netdb.h> #include <netdb.h>
#if defined(POCO_OS_FAMILY_UNIX)
#if (POCO_OS == POCO_OS_LINUX) || (POCO_OS == POCO_OS_ANDROID) #if (POCO_OS == POCO_OS_LINUX) || (POCO_OS == POCO_OS_ANDROID)
// Net/src/NetworkInterface.cpp changed #include <linux/if.h> to #include <net/if.h> // Net/src/NetworkInterface.cpp changed #include <linux/if.h> to #include <net/if.h>
// no more conflict, can use #include <net/if.h> here // no more conflict, can use #include <net/if.h> here
@ -161,7 +168,6 @@
#else #else
#include <net/if.h> #include <net/if.h>
#endif #endif
#endif
#if (POCO_OS == POCO_OS_SOLARIS) || (POCO_OS == POCO_OS_MAC_OS_X) #if (POCO_OS == POCO_OS_SOLARIS) || (POCO_OS == POCO_OS_MAC_OS_X)
#include <sys/sockio.h> #include <sys/sockio.h>
#include <sys/filio.h> #include <sys/filio.h>
@ -228,6 +234,9 @@
#define POCO_TRY_AGAIN TRY_AGAIN #define POCO_TRY_AGAIN TRY_AGAIN
#define POCO_NO_RECOVERY NO_RECOVERY #define POCO_NO_RECOVERY NO_RECOVERY
#define POCO_NO_DATA NO_DATA #define POCO_NO_DATA NO_DATA
#if !defined (POCO_NET_NO_UNIX_SOCKET)
#define POCO_HAS_UNIX_SOCKET
#endif
#endif #endif
@ -389,7 +398,7 @@ struct AddressFamily
{ {
UNKNOWN = AF_UNSPEC, UNKNOWN = AF_UNSPEC,
/// Unspecified family /// Unspecified family
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
UNIX_LOCAL = AF_UNIX, UNIX_LOCAL = AF_UNIX,
/// UNIX domain socket address family. Available on UNIX/POSIX platforms only. /// UNIX domain socket address family. Available on UNIX/POSIX platforms only.
#endif #endif

View File

@ -36,7 +36,8 @@ DatagramSocketImpl::DatagramSocketImpl(SocketAddress::Family family)
else if (family == SocketAddress::IPv6) else if (family == SocketAddress::IPv6)
init(AF_INET6); init(AF_INET6);
#endif #endif
#if defined(POCO_OS_FAMILY_UNIX) // on windows, UDP is not supported for local sockets
#if defined(POCO_OS_FAMILY_UNIX) && defined(POCO_HAS_UNIX_SOCKET)
else if (family == SocketAddress::UNIX_LOCAL) else if (family == SocketAddress::UNIX_LOCAL)
init(AF_UNIX); init(AF_UNIX);
#endif #endif

View File

@ -53,7 +53,7 @@ MulticastSocket::MulticastSocket()
MulticastSocket::MulticastSocket(SocketAddress::Family family): DatagramSocket(family) MulticastSocket::MulticastSocket(SocketAddress::Family family): DatagramSocket(family)
{ {
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
if (family == SocketAddress::UNIX_LOCAL) if (family == SocketAddress::UNIX_LOCAL)
throw Poco::InvalidArgumentException("Cannot create a MulticastSocket with UNIX_LOCAL socket"); throw Poco::InvalidArgumentException("Cannot create a MulticastSocket with UNIX_LOCAL socket");
#endif #endif

View File

@ -14,6 +14,7 @@
#include "Poco/Net/PollSet.h" #include "Poco/Net/PollSet.h"
#include "Poco/Net/SocketImpl.h" #include "Poco/Net/SocketImpl.h"
#include "Poco/TemporaryFile.h"
#include "Poco/Mutex.h" #include "Poco/Mutex.h"
#include <set> #include <set>
@ -68,9 +69,14 @@ public:
using SocketMode = std::pair<Socket, int>; using SocketMode = std::pair<Socket, int>;
using SocketMap = std::map<void*, SocketMode>; using SocketMap = std::map<void*, SocketMode>;
PollSetImpl(): _events(1024), static const epoll_event EPOLL_NULL_EVENT;
_port(0),
_eventfd(eventfd(_port, 0)), PollSetImpl(): _events(FD_SETSIZE, EPOLL_NULL_EVENT),
#if defined(WEPOLL_H_)
_eventfd(eventfd()),
#else
_eventfd(eventfd(0, 0)),
#endif // WEPOLL_H_
_epollfd(epoll_create(1)) _epollfd(epoll_create(1))
{ {
int err = addFD(_eventfd, PollSet::POLL_READ, EPOLL_CTL_ADD); int err = addFD(_eventfd, PollSet::POLL_READ, EPOLL_CTL_ADD);
@ -87,7 +93,6 @@ public:
~PollSetImpl() ~PollSetImpl()
{ {
#ifdef WEPOLL_H_ #ifdef WEPOLL_H_
if (_eventfd >= 0) eventfd(_port, _eventfd);
if (_epollfd) close(_epollfd); if (_epollfd) close(_epollfd);
#else #else
if (_eventfd > 0) close(_eventfd.exchange(0)); if (_eventfd > 0) close(_eventfd.exchange(0));
@ -153,10 +158,7 @@ public:
if (_epollfd < 0) SocketImpl::error(); if (_epollfd < 0) SocketImpl::error();
#endif #endif
} }
#ifdef WEPOLL_H_ #ifndef WEPOLL_H_
eventfd(_port, _eventfd);
_eventfd = eventfd(_port);
#else
close(_eventfd.exchange(0)); close(_eventfd.exchange(0));
_eventfd = eventfd(0, 0); _eventfd = eventfd(0, 0);
#endif #endif
@ -231,10 +233,16 @@ public:
void wakeUp() void wakeUp()
{ {
#ifdef WEPOLL_H_
StreamSocket ss(SocketAddress("127.0.0.1", _port));
#else
uint64_t val = 1; uint64_t val = 1;
#ifdef WEPOLL_H_
#ifdef POCO_HAS_UNIX_SOCKET
poco_check_ptr (_pSockFile);
StreamSocket ss(SocketAddress(_pSockFile->path()));
#else
StreamSocket ss(SocketAddress("127.0.0.1", _port));
#endif
ss.sendBytes(&val, sizeof(val));
#else
// This is guaranteed to write into a valid fd, // This is guaranteed to write into a valid fd,
// or 0 (meaning PollSet is being destroyed). // or 0 (meaning PollSet is being destroyed).
// Errors are ignored. // Errors are ignored.
@ -296,41 +304,46 @@ private:
return epoll_ctl(_epollfd, op, fd, &ev); return epoll_ctl(_epollfd, op, fd, &ev);
} }
#ifdef WEPOLL_H_ #ifndef WEPOLL_H_
using EPollHandle = std::atomic<int>;
#else // WEPOLL_H_
using EPollHandle = std::atomic<HANDLE>;
int eventfd(int& port, int rmFD = 0) #ifdef POCO_HAS_UNIX_SOCKET
{ int eventfd()
if (rmFD == 0)
{ {
_pSocket = new ServerSocket(SocketAddress("127.0.0.1", 0)); if (!_pSockFile)
{
_pSockFile.reset(new TemporaryFile);
_pSocket.reset(new ServerSocket(SocketAddress(_pSockFile->path())));
}
_pSocket->setBlocking(false); _pSocket->setBlocking(false);
port = _pSocket->address().port();
return static_cast<int>(_pSocket->impl()->sockfd()); return static_cast<int>(_pSocket->impl()->sockfd());
} }
else std::unique_ptr<TemporaryFile> _pSockFile;
#else // no unix socket, listen on localhost
int eventfd()
{ {
delete _pSocket; if (!_pSocket)
_pSocket = 0; _pSocket.reset(new ServerSocket(SocketAddress("127.0.0.1", 0)));
port = 0; _port = _pSocket->address().port();
_pSocket->setBlocking(false);
return static_cast<int>(_pSocket->impl()->sockfd());
} }
return 0; int _port = 0;
} #endif // POCO_HAS_UNIX_SOCKET
std::unique_ptr<ServerSocket> _pSocket;
#endif // WEPOLL_H_ #endif // WEPOLL_H_
mutable Mutex _mutex; mutable Mutex _mutex;
SocketMap _socketMap; SocketMap _socketMap;
std::vector<struct epoll_event> _events; std::vector<struct epoll_event> _events;
int _port;
std::atomic<int> _eventfd; std::atomic<int> _eventfd;
#ifdef WEPOLL_H_ EPollHandle _epollfd;
std::atomic <HANDLE> _epollfd;
ServerSocket* _pSocket;
#else
std::atomic<int> _epollfd;
#endif
}; };
const epoll_event PollSetImpl::EPOLL_NULL_EVENT = {0, {0}};
#elif defined(POCO_HAVE_FD_POLL) #elif defined(POCO_HAVE_FD_POLL)

View File

@ -20,12 +20,14 @@
#include "Poco/NumberParser.h" #include "Poco/NumberParser.h"
#include "Poco/BinaryReader.h" #include "Poco/BinaryReader.h"
#include "Poco/BinaryWriter.h" #include "Poco/BinaryWriter.h"
#include "Poco/RegularExpression.h"
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
using Poco::RefCountedObject; using Poco::RefCountedObject;
using Poco::NumberParser; using Poco::NumberParser;
using Poco::RegularExpression;
using Poco::UInt16; using Poco::UInt16;
using Poco::InvalidArgumentException; using Poco::InvalidArgumentException;
using Poco::Net::Impl::SocketAddressImpl; using Poco::Net::Impl::SocketAddressImpl;
@ -33,7 +35,7 @@ using Poco::Net::Impl::IPv4SocketAddressImpl;
#ifdef POCO_HAVE_IPv6 #ifdef POCO_HAVE_IPv6
using Poco::Net::Impl::IPv6SocketAddressImpl; using Poco::Net::Impl::IPv6SocketAddressImpl;
#endif #endif
#ifdef POCO_OS_FAMILY_UNIX #ifdef POCO_HAS_UNIX_SOCKET
using Poco::Net::Impl::LocalSocketAddressImpl; using Poco::Net::Impl::LocalSocketAddressImpl;
#endif #endif
@ -63,7 +65,7 @@ const SocketAddress::Family SocketAddress::IPv4;
#if defined(POCO_HAVE_IPv6) #if defined(POCO_HAVE_IPv6)
const SocketAddress::Family SocketAddress::IPv6; const SocketAddress::Family SocketAddress::IPv6;
#endif #endif
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
const SocketAddress::Family SocketAddress::UNIX_LOCAL; const SocketAddress::Family SocketAddress::UNIX_LOCAL;
#endif #endif
#endif #endif
@ -143,7 +145,7 @@ SocketAddress::SocketAddress(const SocketAddress& socketAddress)
else if (socketAddress.family() == IPv6) else if (socketAddress.family() == IPv6)
newIPv6(reinterpret_cast<const sockaddr_in6*>(socketAddress.addr())); newIPv6(reinterpret_cast<const sockaddr_in6*>(socketAddress.addr()));
#endif #endif
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
else if (socketAddress.family() == UNIX_LOCAL) else if (socketAddress.family() == UNIX_LOCAL)
newLocal(reinterpret_cast<const sockaddr_un*>(socketAddress.addr())); newLocal(reinterpret_cast<const sockaddr_un*>(socketAddress.addr()));
#endif #endif
@ -164,7 +166,7 @@ SocketAddress::SocketAddress(const struct sockaddr* sockAddr, poco_socklen_t len
else if (length == sizeof(struct sockaddr_in6) && sockAddr->sa_family == AF_INET6) else if (length == sizeof(struct sockaddr_in6) && sockAddr->sa_family == AF_INET6)
newIPv6(reinterpret_cast<const struct sockaddr_in6*>(sockAddr)); newIPv6(reinterpret_cast<const struct sockaddr_in6*>(sockAddr));
#endif #endif
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
else if (length > 0 && length <= sizeof(struct sockaddr_un) && sockAddr->sa_family == AF_UNIX) else if (length > 0 && length <= sizeof(struct sockaddr_un) && sockAddr->sa_family == AF_UNIX)
newLocal(reinterpret_cast<const sockaddr_un*>(sockAddr)); newLocal(reinterpret_cast<const sockaddr_un*>(sockAddr));
#endif #endif
@ -181,7 +183,7 @@ bool SocketAddress::operator < (const SocketAddress& socketAddress) const
{ {
if (family() < socketAddress.family()) return true; if (family() < socketAddress.family()) return true;
if (family() > socketAddress.family()) return false; if (family() > socketAddress.family()) return false;
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
if (family() == UNIX_LOCAL) return toString() < socketAddress.toString(); if (family() == UNIX_LOCAL) return toString() < socketAddress.toString();
#endif #endif
if (host() < socketAddress.host()) return true; if (host() < socketAddress.host()) return true;
@ -200,7 +202,7 @@ SocketAddress& SocketAddress::operator = (const SocketAddress& socketAddress)
else if (socketAddress.family() == IPv6) else if (socketAddress.family() == IPv6)
newIPv6(reinterpret_cast<const sockaddr_in6*>(socketAddress.addr())); newIPv6(reinterpret_cast<const sockaddr_in6*>(socketAddress.addr()));
#endif #endif
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
else if (socketAddress.family() == UNIX_LOCAL) else if (socketAddress.family() == UNIX_LOCAL)
newLocal(reinterpret_cast<const sockaddr_un*>(socketAddress.addr())); newLocal(reinterpret_cast<const sockaddr_un*>(socketAddress.addr()));
#endif #endif
@ -325,7 +327,7 @@ void SocketAddress::init(Family fam, const std::string& hostAddress, Poco::UInt1
void SocketAddress::init(Family fam, const std::string& address) void SocketAddress::init(Family fam, const std::string& address)
{ {
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
if (fam == UNIX_LOCAL) if (fam == UNIX_LOCAL)
{ {
newLocal(address); newLocal(address);
@ -364,6 +366,15 @@ void SocketAddress::init(const std::string& hostAndPort)
{ {
poco_assert (!hostAndPort.empty()); poco_assert (!hostAndPort.empty());
#if defined(POCO_OS_FAMILY_WINDOWS) && defined(POCO_HAS_UNIX_SOCKET)
RegularExpression re(R"((?:[a-zA-Z]\:|\\\\[\w\s\.]+\\[\w\s\.$]+)\\(?:[\w\s\.]+\\)*[\w\s\.]*?$)");
if (re.match(hostAndPort))
{
newLocal(hostAndPort);
return;
}
#endif
std::string host; std::string host;
std::string port; std::string port;
std::string::const_iterator it = hostAndPort.begin(); std::string::const_iterator it = hostAndPort.begin();

View File

@ -127,7 +127,7 @@ std::string IPv6SocketAddressImpl::toString() const
#endif // POCO_HAVE_IPv6 #endif // POCO_HAVE_IPv6
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
// //
@ -146,22 +146,36 @@ LocalSocketAddressImpl::LocalSocketAddressImpl(const char* path)
{ {
poco_assert (std::strlen(path) < sizeof(_pAddr->sun_path)); poco_assert (std::strlen(path) < sizeof(_pAddr->sun_path));
#if POCO_OS != POCO_OS_LINUX
if (path[0] == 0)
throw Poco::InvalidArgumentException("LocalSocketAddressImpl(): "
"abstract sockets are only supported on Linux");
#endif
_pAddr = new sockaddr_un; _pAddr = new sockaddr_un;
poco_set_sun_len(_pAddr, std::strlen(path) + sizeof(struct sockaddr_un) - sizeof(_pAddr->sun_path) + 1); std::memset(_pAddr, 0, sizeof(sockaddr_un));
std::size_t length = strlen(path);
poco_set_sun_len(_pAddr, length + sizeof(struct sockaddr_un) - sizeof(_pAddr->sun_path) + 1);
_pAddr->sun_family = AF_UNIX; _pAddr->sun_family = AF_UNIX;
std::strcpy(_pAddr->sun_path, path); std::memcpy(&_pAddr->sun_path, path, length);
} }
LocalSocketAddressImpl::LocalSocketAddressImpl(const char* path, std::size_t length) LocalSocketAddressImpl::LocalSocketAddressImpl(const char* path, std::size_t length)
{ {
poco_assert (length < sizeof(_pAddr->sun_path)); poco_assert (length && length < sizeof(_pAddr->sun_path));
#if POCO_OS != POCO_OS_LINUX
if (path[0] == 0)
throw Poco::InvalidArgumentException("LocalSocketAddressImpl(): "
"abstract sockets are only supported on Linux");
#endif
_pAddr = new sockaddr_un; _pAddr = new sockaddr_un;
std::memset(_pAddr, 0, sizeof(sockaddr_un));
poco_set_sun_len(_pAddr, length + sizeof(struct sockaddr_un) - sizeof(_pAddr->sun_path) + 1); poco_set_sun_len(_pAddr, length + sizeof(struct sockaddr_un) - sizeof(_pAddr->sun_path) + 1);
_pAddr->sun_family = AF_UNIX; _pAddr->sun_family = AF_UNIX;
std::memcpy(_pAddr->sun_path, path, length); std::memcpy(&_pAddr->sun_path, path, length);
_pAddr->sun_path[length] = 0;
} }
@ -178,7 +192,7 @@ std::string LocalSocketAddressImpl::toString() const
} }
#endif // POCO_OS_FAMILY_UNIX #endif // POCO_HAS_UNIX_SOCKET
} } } // namespace Poco::Net::Impl } } } // namespace Poco::Net::Impl

View File

@ -227,8 +227,15 @@ void SocketImpl::bind(const SocketAddress& address, bool reuseAddress, bool reus
{ {
init(address.af()); init(address.af());
} }
setReuseAddress(reuseAddress);
setReusePort(reusePort); #ifdef POCO_HAS_UNIX_SOCKET
if (address.family() != SocketAddress::Family::UNIX_LOCAL)
#endif
{
setReuseAddress(reuseAddress);
setReusePort(reusePort);
}
#if defined(POCO_VXWORKS) #if defined(POCO_VXWORKS)
int rc = ::bind(_sockfd, (sockaddr*) address.addr(), address.length()); int rc = ::bind(_sockfd, (sockaddr*) address.addr(), address.length());
#else #else

View File

@ -34,7 +34,7 @@ StreamSocketImpl::StreamSocketImpl(SocketAddress::Family family)
else if (family == SocketAddress::IPv6) else if (family == SocketAddress::IPv6)
init(AF_INET6); init(AF_INET6);
#endif #endif
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
else if (family == SocketAddress::UNIX_LOCAL) else if (family == SocketAddress::UNIX_LOCAL)
init(AF_UNIX); init(AF_UNIX);
#endif #endif

View File

@ -139,7 +139,7 @@ void TCPServer::run()
if (!_pConnectionFilter || _pConnectionFilter->accept(ss)) if (!_pConnectionFilter || _pConnectionFilter->accept(ss))
{ {
// enable nodelay per default: OSX really needs that // enable nodelay per default: OSX really needs that
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
if (ss.address().family() != AddressFamily::UNIX_LOCAL) if (ss.address().family() != AddressFamily::UNIX_LOCAL)
#endif #endif
{ {

View File

@ -11,7 +11,9 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
// //
#ifndef NOMINMAX
#define NOMINMAX
#endif // NOMINMAX
#include "Poco/Net/WebSocketImpl.h" #include "Poco/Net/WebSocketImpl.h"
#include "Poco/Net/NetException.h" #include "Poco/Net/NetException.h"
#include "Poco/Net/WebSocket.h" #include "Poco/Net/WebSocket.h"

View File

@ -72,6 +72,11 @@ void EchoServer::run()
{ {
ss.sendBytes(buffer, n); ss.sendBytes(buffer, n);
n = ss.receiveBytes(buffer, sizeof(buffer)); n = ss.receiveBytes(buffer, sizeof(buffer));
if (n == 0)
{
_stop = true;
break;
}
} }
} }
catch (Poco::Exception& exc) catch (Poco::Exception& exc)

View File

@ -326,10 +326,9 @@ void PollSetTest::testPoll()
void PollSetTest::testPollNoServer() void PollSetTest::testPollNoServer()
{ {
StreamSocket ss1; StreamSocket ss1(SocketAddress::IPv4);
StreamSocket ss2; StreamSocket ss2(SocketAddress::IPv4);
ss1.connectNB(SocketAddress("127.0.0.1", 0xFEFE));
ss2.connectNB(SocketAddress("127.0.0.1", 0xFEFF));
PollSet ps; PollSet ps;
assertTrue(ps.empty()); assertTrue(ps.empty());
ps.add(ss1, PollSet::POLL_READ | PollSet::POLL_WRITE | PollSet::POLL_ERROR); ps.add(ss1, PollSet::POLL_READ | PollSet::POLL_WRITE | PollSet::POLL_ERROR);
@ -337,26 +336,35 @@ void PollSetTest::testPollNoServer()
assertTrue(!ps.empty()); assertTrue(!ps.empty());
assertTrue(ps.has(ss1)); assertTrue(ps.has(ss1));
assertTrue(ps.has(ss2)); assertTrue(ps.has(ss2));
// should be like this, but Linux epoll disagrees ...
//assertEqual(0, static_cast<int>(ps.poll(Timespan(100)).size()));
ss1.connectNB(SocketAddress("127.0.0.1", 0xFEFE));
ss2.connectNB(SocketAddress("127.0.0.1", 0xFEFF));
PollSet::SocketModeMap sm; PollSet::SocketModeMap sm;
int signalled = 0; int signalledErrors = 0;
Stopwatch sw; sw.start(); Stopwatch sw; sw.start();
do do
{ {
sm = ps.poll(Timespan(1000000)); sm = ps.poll(Timespan(1000000));
for (auto s : sm) for (auto s : sm)
{
assertTrue(0 != (s.second & PollSet::POLL_ERROR)); assertTrue(0 != (s.second & PollSet::POLL_ERROR));
++signalledErrors;
}
signalled += static_cast<int>(sm.size());
sm.clear(); sm.clear();
int secs = sw.elapsedSeconds(); int secs = sw.elapsedSeconds();
if (secs > 10) if (secs > 10)
{ {
fail(Poco::format("testPollNoServer() timed out after %ds, " fail(Poco::format("testPollNoServer() timed out after %ds, "
"sockets signalled=%d (expected 2)", "sockets signalled=%d (expected 2)",
secs, signalled), __LINE__); secs, signalledErrors), __LINE__);
} }
} while (signalled < 2); } while (signalledErrors < 2);
assertTrue(signalled == 2); assertEqual(2, signalledErrors);
} }
@ -365,11 +373,9 @@ void PollSetTest::testPollClosedServer()
{ {
EchoServer echoServer1; EchoServer echoServer1;
EchoServer echoServer2; EchoServer echoServer2;
StreamSocket ss1;
StreamSocket ss2;
ss1.connect(SocketAddress("127.0.0.1", echoServer1.port())); StreamSocket ss1(SocketAddress::IPv4);
ss2.connect(SocketAddress("127.0.0.1", echoServer2.port())); StreamSocket ss2(SocketAddress::IPv4);
PollSet ps; PollSet ps;
assertTrue(ps.empty()); assertTrue(ps.empty());
@ -379,6 +385,11 @@ void PollSetTest::testPollClosedServer()
assertTrue(ps.has(ss1)); assertTrue(ps.has(ss1));
assertTrue(ps.has(ss2)); assertTrue(ps.has(ss2));
//assertEqual(0, static_cast<int>(ps.poll(Timespan(100)).size()));
ss1.connect(SocketAddress("127.0.0.1", echoServer1.port()));
ss2.connect(SocketAddress("127.0.0.1", echoServer2.port()));
std::string str = "HELLO"; std::string str = "HELLO";
int len = static_cast<int>(str.length()); int len = static_cast<int>(str.length());
@ -393,14 +404,23 @@ void PollSetTest::testPollClosedServer()
int secs = sw.elapsedSeconds(); int secs = sw.elapsedSeconds();
if (secs > 10) if (secs > 10)
{ {
fail(Poco::format("testPollClosedServer() timed out " fail(Poco::format("testPollClosedServer(1) timed out "
"waiting on server after %ds", secs), __LINE__); "waiting on server after %ds", secs), __LINE__);
} }
} }
echoServer2.stop(); echoServer2.stop();
assertTrue (len == ss2.sendBytes(str.data(), len)); assertTrue (len == ss2.sendBytes(str.data(), len));
while (!echoServer2.done()) Thread::sleep(10); while (!echoServer2.done())
{
Thread::sleep(10);
int secs = sw.elapsedSeconds();
if (secs > 10)
{
fail(Poco::format("testPollClosedServer(2) timed out "
"waiting on server after %ds", secs), __LINE__);
}
}
int signalled = 0; int signalled = 0;
sw.restart(); sw.restart();

View File

@ -11,6 +11,7 @@
#include "SocketAddressTest.h" #include "SocketAddressTest.h"
#include "CppUnit/TestCaller.h" #include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h" #include "CppUnit/TestSuite.h"
#include "Poco/Path.h"
#include "Poco/Net/SocketAddress.h" #include "Poco/Net/SocketAddress.h"
#include "Poco/Net/NetException.h" #include "Poco/Net/NetException.h"
#include <iostream> #include <iostream>
@ -23,6 +24,7 @@ using Poco::Net::HostNotFoundException;
using Poco::Net::ServiceNotFoundException; using Poco::Net::ServiceNotFoundException;
using Poco::Net::NoAddressFoundException; using Poco::Net::NoAddressFoundException;
using Poco::Net::AddressFamilyMismatchException; using Poco::Net::AddressFamilyMismatchException;
using Poco::Path;
using Poco::InvalidArgumentException; using Poco::InvalidArgumentException;
@ -184,25 +186,27 @@ void SocketAddressTest::testSocketAddress6()
void SocketAddressTest::testSocketAddressUnixLocal() void SocketAddressTest::testSocketAddressUnixLocal()
{ {
#ifdef POCO_OS_FAMILY_UNIX #ifdef POCO_HAS_UNIX_SOCKET
SocketAddress sa1(SocketAddress::UNIX_LOCAL, "/tmp/sock1"); std::string name1 = Path::tempHome() + "sock1";
SocketAddress sa1(SocketAddress::UNIX_LOCAL, name1);
assertTrue (sa1.af() == AF_UNIX); assertTrue (sa1.af() == AF_UNIX);
assertTrue (sa1.family() == SocketAddress::UNIX_LOCAL); assertTrue (sa1.family() == SocketAddress::UNIX_LOCAL);
assertTrue (sa1.toString() == "/tmp/sock1"); assertTrue (sa1.toString() == name1);
SocketAddress sa2(SocketAddress::UNIX_LOCAL, "/tmp/sock2"); std::string name2 = Path::tempHome() + "sock2";
SocketAddress sa2(SocketAddress::UNIX_LOCAL, name2);
assertTrue (sa1 != sa2); assertTrue (sa1 != sa2);
assertTrue (sa1 < sa2); assertTrue (sa1 < sa2);
SocketAddress sa3(SocketAddress::UNIX_LOCAL, "/tmp/sock1"); SocketAddress sa3(SocketAddress::UNIX_LOCAL, name1);
assertTrue (sa1 == sa3); assertTrue (sa1 == sa3);
assertTrue (!(sa1 < sa3)); assertTrue (!(sa1 < sa3));
SocketAddress sa4("/tmp/sock1"); SocketAddress sa4(name1);
assertTrue (sa1 == sa4); assertTrue (sa1 == sa4);
assertTrue (sa4.toString() == "/tmp/sock1"); assertTrue (sa4.toString() == name1);
#else #else
std::cout << "[UNIX LOCAL DISABLED]" << std::endl; std::cout << "[UNIX LOCAL SOCKET DISABLED]" << std::endl;
#endif #endif
} }

View File

@ -22,6 +22,7 @@
#include "Poco/FIFOBuffer.h" #include "Poco/FIFOBuffer.h"
#include "Poco/Delegate.h" #include "Poco/Delegate.h"
#include "Poco/File.h" #include "Poco/File.h"
#include "Poco/Path.h"
#include <iostream> #include <iostream>
@ -36,6 +37,8 @@ using Poco::TimeoutException;
using Poco::InvalidArgumentException; using Poco::InvalidArgumentException;
using Poco::Buffer; using Poco::Buffer;
using Poco::FIFOBuffer; using Poco::FIFOBuffer;
using Poco::Path;
using Poco::File;
using Poco::delegate; using Poco::delegate;
@ -536,12 +539,14 @@ void SocketTest::testSelect3()
void SocketTest::testEchoUnixLocal() void SocketTest::testEchoUnixLocal()
{ {
#if defined(POCO_OS_FAMILY_UNIX) #if defined(POCO_HAS_UNIX_SOCKET)
#if POCO_OS == POCO_OS_ANDROID #if POCO_OS == POCO_OS_ANDROID
Poco::File socketFile("/data/local/tmp/SocketTest.sock"); File socketFile("/data/local/tmp/SocketTest.sock");
#elif defined(POCO_OS_FAMILY_WINDOWS)
File socketFile(Path::tempHome() + "SocketTest.sock");
#else #else
Poco::File socketFile("/tmp/SocketTest.sock"); File socketFile("/tmp/SocketTest.sock");
#endif #endif // POCO_OS == POCO_OS_ANDROID
if (socketFile.exists()) socketFile.remove(); if (socketFile.exists()) socketFile.remove();
SocketAddress localAddr(SocketAddress::UNIX_LOCAL, socketFile.path()); SocketAddress localAddr(SocketAddress::UNIX_LOCAL, socketFile.path());
EchoServer echoServer(localAddr); EchoServer echoServer(localAddr);
@ -554,7 +559,37 @@ void SocketTest::testEchoUnixLocal()
assertTrue (n == 5); assertTrue (n == 5);
assertTrue (std::string(buffer, n) == "hello"); assertTrue (std::string(buffer, n) == "hello");
ss.close(); ss.close();
socketFile.remove(); if (socketFile.exists()) socketFile.remove();
echoServer.stop();
#else // POCO_HAS_UNIX_SOCKET
#pragma message("[UNIX LOCAL SOCKET DISABLED]")
std::cout << "[UNIX LOCAL SOCKET DISABLED]" << std::endl;
#endif
}
void SocketTest::testUnixLocalAbstract()
{
// abstract local sockets don't work on windows
// see https://github.com/microsoft/WSL/issues/4240
// they are a nonportable Linux extension
#if (POCO_OS == POCO_OS_LINUX) && defined(POCO_HAS_UNIX_SOCKET)
std::string addr("\0look ma - no file!", 20);
SocketAddress localAddr(SocketAddress::UNIX_LOCAL, addr);
EchoServer echoServer(localAddr);
StreamSocket ss(SocketAddress::UNIX_LOCAL);
ss.connect(localAddr);
int n = ss.sendBytes("hello", 5);
assertTrue(n == 5);
char buffer[256];
n = ss.receiveBytes(buffer, sizeof(buffer));
assertTrue(n == 5);
assertTrue(std::string(buffer, n) == "hello");
ss.close();
echoServer.stop();
#else // POCO_HAS_UNIX_SOCKET
#pragma message("[ABSTRACT UNIX LOCAL SOCKET DISABLED]")
std::cout << "[ABSTRACT UNIX LOCAL SOCKET DISABLED]" << std::endl;
#endif #endif
} }
@ -610,6 +645,7 @@ CppUnit::Test* SocketTest::suite()
CppUnit_addTest(pSuite, SocketTest, testSelect2); CppUnit_addTest(pSuite, SocketTest, testSelect2);
CppUnit_addTest(pSuite, SocketTest, testSelect3); CppUnit_addTest(pSuite, SocketTest, testSelect3);
CppUnit_addTest(pSuite, SocketTest, testEchoUnixLocal); CppUnit_addTest(pSuite, SocketTest, testEchoUnixLocal);
CppUnit_addTest(pSuite, SocketTest, testUnixLocalAbstract);
return pSuite; return pSuite;
} }

View File

@ -42,6 +42,7 @@ public:
void testSelect2(); void testSelect2();
void testSelect3(); void testSelect3();
void testEchoUnixLocal(); void testEchoUnixLocal();
void testUnixLocalAbstract();
void setUp(); void setUp();
void tearDown(); void tearDown();