Asynchronous resolving.
This commit is contained in:
parent
baec395c8c
commit
194a797055
@ -1003,6 +1003,19 @@ ELSE(PTHREAD_LIB)
|
|||||||
SET(VMIME_HAVE_PTHREAD 0)
|
SET(VMIME_HAVE_PTHREAD 0)
|
||||||
ENDIF(PTHREAD_LIB)
|
ENDIF(PTHREAD_LIB)
|
||||||
|
|
||||||
|
# getaddrinfo_a() - GNU libc
|
||||||
|
LIST(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
|
||||||
|
LIST(APPEND CMAKE_REQUIRED_LIBRARIES anl)
|
||||||
|
CHECK_SYMBOL_EXISTS(getaddrinfo_a netdb.h VMIME_HAVE_GETADDRINFO_A)
|
||||||
|
|
||||||
|
IF(VMIME_HAVE_GETADDRINFO_A)
|
||||||
|
TARGET_LINK_LIBRARIES(
|
||||||
|
${VMIME_LIBRARY_NAME}
|
||||||
|
${TARGET_LINK_LIBRARIES}
|
||||||
|
anl
|
||||||
|
)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# Additional compiler flags
|
# Additional compiler flags
|
||||||
|
@ -76,6 +76,7 @@ typedef unsigned @VMIME_64BIT_TYPE@ vmime_uint64;
|
|||||||
#cmakedefine01 VMIME_PLATFORM_IS_WINDOWS
|
#cmakedefine01 VMIME_PLATFORM_IS_WINDOWS
|
||||||
#cmakedefine01 VMIME_HAVE_PTHREAD
|
#cmakedefine01 VMIME_HAVE_PTHREAD
|
||||||
#cmakedefine01 VMIME_HAVE_GETADDRINFO
|
#cmakedefine01 VMIME_HAVE_GETADDRINFO
|
||||||
|
#cmakedefine01 VMIME_HAVE_GETADDRINFO_A
|
||||||
#cmakedefine01 VMIME_HAVE_GETTID
|
#cmakedefine01 VMIME_HAVE_GETTID
|
||||||
#cmakedefine01 VMIME_HAVE_SYSCALL
|
#cmakedefine01 VMIME_HAVE_SYSCALL
|
||||||
#cmakedefine01 VMIME_HAVE_SYSCALL_GETTID
|
#cmakedefine01 VMIME_HAVE_SYSCALL_GETTID
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
|
||||||
/** Time out handler.
|
/** Time out handler.
|
||||||
@ -8,19 +9,27 @@ class timeoutHandler : public vmime::net::timeoutHandler
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
timeoutHandler()
|
||||||
|
: m_start(time(NULL))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool isTimeOut()
|
bool isTimeOut()
|
||||||
{
|
{
|
||||||
// This is a cancellation point: return true if you want to cancel
|
// This is a cancellation point: return true if you want to cancel
|
||||||
// the current operation. If you return true, handleTimeOut() will
|
// the current operation. If you return true, handleTimeOut() will
|
||||||
// be called just after this, and before actually cancelling the
|
// be called just after this, and before actually cancelling the
|
||||||
// operation
|
// operation
|
||||||
return false;
|
|
||||||
|
// 10 seconds timeout
|
||||||
|
return (time(NULL) - m_start) >= 10; // seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetTimeOut()
|
void resetTimeOut()
|
||||||
{
|
{
|
||||||
// Called at the beginning of an operation (eg. connecting,
|
// Called at the beginning of an operation (eg. connecting,
|
||||||
// a read() or a write() on a socket...)
|
// a read() or a write() on a socket...)
|
||||||
|
m_start = time(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handleTimeOut()
|
bool handleTimeOut()
|
||||||
@ -29,10 +38,14 @@ public:
|
|||||||
// allows you to interact with the user, ie. display a prompt to
|
// allows you to interact with the user, ie. display a prompt to
|
||||||
// know whether he wants to cancel the operation.
|
// know whether he wants to cancel the operation.
|
||||||
|
|
||||||
// If you return true here, the operation will be actually cancelled.
|
// If you return false here, the operation will be actually cancelled.
|
||||||
// If not, the time out is reset and the operation continues.
|
// If true, the time out is reset and the operation continues.
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
time_t m_start;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,6 +30,10 @@
|
|||||||
#include "vmime/platforms/posix/posixSocket.hpp"
|
#include "vmime/platforms/posix/posixSocket.hpp"
|
||||||
#include "vmime/platforms/posix/posixHandler.hpp"
|
#include "vmime/platforms/posix/posixHandler.hpp"
|
||||||
|
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE // for getaddrinfo_a() in <netdb.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
@ -95,42 +99,26 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
#if VMIME_HAVE_GETADDRINFO // use thread-safe and IPv6-aware getaddrinfo() if available
|
#if VMIME_HAVE_GETADDRINFO // use thread-safe and IPv6-aware getaddrinfo() if available
|
||||||
|
|
||||||
// Resolve address, if needed
|
// Resolve address, if needed
|
||||||
struct ::addrinfo hints;
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
|
||||||
|
|
||||||
hints.ai_flags = AI_CANONNAME;
|
|
||||||
hints.ai_family = PF_UNSPEC;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
|
|
||||||
std::ostringstream portStr;
|
|
||||||
portStr.imbue(std::locale::classic());
|
|
||||||
|
|
||||||
portStr << port;
|
|
||||||
|
|
||||||
struct ::addrinfo* res0;
|
|
||||||
|
|
||||||
if (::getaddrinfo(address.c_str(), portStr.str().c_str(), &hints, &res0) != 0)
|
|
||||||
{
|
|
||||||
// Error: cannot resolve address
|
|
||||||
throw vmime::exceptions::connection_error("Cannot resolve address.");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_serverAddress = address;
|
m_serverAddress = address;
|
||||||
|
|
||||||
|
struct ::addrinfo* addrInfo = NULL; // resolved addresses
|
||||||
|
resolve(&addrInfo, address, port);
|
||||||
|
|
||||||
// Connect to host
|
// Connect to host
|
||||||
int sock = -1;
|
int sock = -1;
|
||||||
struct ::addrinfo* res = res0;
|
|
||||||
int connectErrno = 0;
|
int connectErrno = 0;
|
||||||
|
|
||||||
if (m_timeoutHandler != NULL)
|
if (m_timeoutHandler != NULL)
|
||||||
m_timeoutHandler->resetTimeOut();
|
m_timeoutHandler->resetTimeOut();
|
||||||
|
|
||||||
for ( ; sock == -1 && res != NULL ; res = res->ai_next, connectErrno = ETIMEDOUT)
|
for (struct ::addrinfo* curAddrInfo = addrInfo ;
|
||||||
|
sock == -1 && curAddrInfo != NULL ;
|
||||||
|
curAddrInfo = curAddrInfo->ai_next, connectErrno = ETIMEDOUT)
|
||||||
{
|
{
|
||||||
if (res->ai_family != AF_INET && res->ai_family != AF_INET6)
|
if (curAddrInfo->ai_family != AF_INET && curAddrInfo->ai_family != AF_INET6)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
sock = ::socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
sock = ::socket(curAddrInfo->ai_family, curAddrInfo->ai_socktype, curAddrInfo->ai_protocol);
|
||||||
|
|
||||||
if (sock < 0)
|
if (sock < 0)
|
||||||
{
|
{
|
||||||
@ -152,7 +140,7 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
{
|
{
|
||||||
::fcntl(sock, F_SETFL, ::fcntl(sock, F_GETFL) | O_NONBLOCK);
|
::fcntl(sock, F_SETFL, ::fcntl(sock, F_GETFL) | O_NONBLOCK);
|
||||||
|
|
||||||
if (::connect(sock, res->ai_addr, res->ai_addrlen) < 0)
|
if (::connect(sock, curAddrInfo->ai_addr, curAddrInfo->ai_addrlen) < 0)
|
||||||
{
|
{
|
||||||
switch (errno)
|
switch (errno)
|
||||||
{
|
{
|
||||||
@ -254,7 +242,7 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
timeval curTime = { 0, 0 };
|
timeval curTime = { 0, 0 };
|
||||||
gettimeofday(&curTime, /* timezone */ NULL);
|
gettimeofday(&curTime, /* timezone */ NULL);
|
||||||
|
|
||||||
if (res->ai_next != NULL &&
|
if (curAddrInfo->ai_next != NULL &&
|
||||||
curTime.tv_usec - startTime.tv_usec >= tryNextTimeout * 1000)
|
curTime.tv_usec - startTime.tv_usec >= tryNextTimeout * 1000)
|
||||||
{
|
{
|
||||||
connectErrno = ETIMEDOUT;
|
connectErrno = ETIMEDOUT;
|
||||||
@ -280,7 +268,7 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (::connect(sock, res->ai_addr, res->ai_addrlen) < 0)
|
if (::connect(sock, curAddrInfo->ai_addr, curAddrInfo->ai_addrlen) < 0)
|
||||||
{
|
{
|
||||||
connectErrno = errno;
|
connectErrno = errno;
|
||||||
::close(sock);
|
::close(sock);
|
||||||
@ -290,7 +278,7 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::freeaddrinfo(res0);
|
::freeaddrinfo(addrInfo);
|
||||||
|
|
||||||
if (sock == -1)
|
if (sock == -1)
|
||||||
{
|
{
|
||||||
@ -373,6 +361,101 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void posixSocket::resolve(struct ::addrinfo** addrInfo, const vmime::string& address, const vmime::port_t port)
|
||||||
|
{
|
||||||
|
std::ostringstream portStr;
|
||||||
|
portStr.imbue(std::locale::classic());
|
||||||
|
portStr << port;
|
||||||
|
|
||||||
|
|
||||||
|
struct ::addrinfo hints;
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
|
||||||
|
hints.ai_flags = AI_CANONNAME | AI_NUMERICSERV;
|
||||||
|
hints.ai_family = PF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
#if VMIME_HAVE_GETADDRINFO_A
|
||||||
|
|
||||||
|
// If getaddrinfo_a() is available, use asynchronous resolving to allow
|
||||||
|
// the timeout handler to cancel the operation
|
||||||
|
|
||||||
|
struct ::gaicb gaiRequest;
|
||||||
|
memset(&gaiRequest, 0, sizeof(gaiRequest));
|
||||||
|
|
||||||
|
gaiRequest.ar_name = address.c_str();
|
||||||
|
gaiRequest.ar_service = portStr.str().c_str();
|
||||||
|
gaiRequest.ar_request = &hints;
|
||||||
|
|
||||||
|
struct ::gaicb* gaiRequests = &gaiRequest;
|
||||||
|
int gaiError;
|
||||||
|
|
||||||
|
if ((gaiError = getaddrinfo_a(GAI_NOWAIT, &gaiRequests, 1, NULL)) != 0)
|
||||||
|
{
|
||||||
|
throw vmime::exceptions::connection_error
|
||||||
|
("getaddrinfo_a() failed: " + std::string(gai_strerror(gaiError)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_timeoutHandler != NULL)
|
||||||
|
m_timeoutHandler->resetTimeOut();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
struct timespec gaiTimeout;
|
||||||
|
gaiTimeout.tv_sec = 1; // query timeout handler every second
|
||||||
|
gaiTimeout.tv_nsec = 0;
|
||||||
|
|
||||||
|
gaiError = gai_suspend(&gaiRequests, 1, &gaiTimeout);
|
||||||
|
|
||||||
|
if (gaiError == 0 || gaiError == EAI_ALLDONE)
|
||||||
|
{
|
||||||
|
const int ret = gai_error(&gaiRequest);
|
||||||
|
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
throw vmime::exceptions::connection_error
|
||||||
|
("getaddrinfo_a() request failed: " + std::string(gai_strerror(ret)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*addrInfo = gaiRequest.ar_result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (gaiError != EAI_AGAIN)
|
||||||
|
{
|
||||||
|
throw vmime::exceptions::connection_error
|
||||||
|
("gai_suspend() failed: " + std::string(gai_strerror(gaiError)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for timeout
|
||||||
|
if (m_timeoutHandler && m_timeoutHandler->isTimeOut())
|
||||||
|
{
|
||||||
|
if (!m_timeoutHandler->handleTimeOut())
|
||||||
|
{
|
||||||
|
throw exceptions::operation_timed_out();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Reset timeout and keep waiting for connection
|
||||||
|
m_timeoutHandler->resetTimeOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // !VMIME_HAVE_GETADDRINFO_A
|
||||||
|
|
||||||
|
if (::getaddrinfo(address.c_str(), portStr.str().c_str(), &hints, addrInfo) != 0)
|
||||||
|
{
|
||||||
|
// Error: cannot resolve address
|
||||||
|
throw vmime::exceptions::connection_error("Cannot resolve address.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // VMIME_HAVE_GETADDRINFO_A
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool posixSocket::isConnected() const
|
bool posixSocket::isConnected() const
|
||||||
{
|
{
|
||||||
if (m_desc == -1)
|
if (m_desc == -1)
|
||||||
|
@ -34,6 +34,9 @@
|
|||||||
#include "vmime/net/socket.hpp"
|
#include "vmime/net/socket.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
struct addrinfo;
|
||||||
|
|
||||||
|
|
||||||
namespace vmime {
|
namespace vmime {
|
||||||
namespace platforms {
|
namespace platforms {
|
||||||
namespace posix {
|
namespace posix {
|
||||||
@ -75,6 +78,8 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
void resolve(struct ::addrinfo** addrInfo, const vmime::string& address, const vmime::port_t port);
|
||||||
|
|
||||||
bool waitForData(const bool read, const bool write, const int msecs);
|
bool waitForData(const bool read, const bool write, const int msecs);
|
||||||
|
|
||||||
static void throwSocketError(const int err);
|
static void throwSocketError(const int err);
|
||||||
|
Loading…
Reference in New Issue
Block a user