Better polling. Fixed possible connection issues on POSIX with slow network. Better error handling in POSIX sockets.
This commit is contained in:
parent
595259332a
commit
0d20ee083b
@ -5748,7 +5748,11 @@ public:
|
|||||||
|
|
||||||
if (receiveBuffer.empty()) // buffer is empty
|
if (receiveBuffer.empty()) // buffer is empty
|
||||||
{
|
{
|
||||||
platform::getHandler()->wait();
|
if (sok->getStatus() & socket::STATUS_WANT_WRITE)
|
||||||
|
sok->waitForWrite();
|
||||||
|
else
|
||||||
|
sok->waitForRead();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5807,7 +5811,11 @@ public:
|
|||||||
|
|
||||||
if (receiveBuffer.empty()) // buffer is empty
|
if (receiveBuffer.empty()) // buffer is empty
|
||||||
{
|
{
|
||||||
platform::getHandler()->wait();
|
if (sok->getStatus() & socket::STATUS_WANT_WRITE)
|
||||||
|
sok->waitForWrite();
|
||||||
|
else
|
||||||
|
sok->waitForRead();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +178,11 @@ void POP3Response::readResponseImpl(string& buffer, const bool multiLine)
|
|||||||
|
|
||||||
if (receiveBuffer.empty()) // buffer is empty
|
if (receiveBuffer.empty()) // buffer is empty
|
||||||
{
|
{
|
||||||
platform::getHandler()->wait();
|
if (m_socket->getStatus() & socket::STATUS_WANT_WRITE)
|
||||||
|
m_socket->waitForWrite();
|
||||||
|
else
|
||||||
|
m_socket->waitForRead();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +273,11 @@ void POP3Response::readResponseImpl
|
|||||||
|
|
||||||
if (read == 0) // buffer is empty
|
if (read == 0) // buffer is empty
|
||||||
{
|
{
|
||||||
platform::getHandler()->wait();
|
if (m_socket->getStatus() & socket::STATUS_WANT_WRITE)
|
||||||
|
m_socket->waitForWrite();
|
||||||
|
else
|
||||||
|
m_socket->waitForRead();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ const string SMTPResponse::readResponseLine()
|
|||||||
|
|
||||||
if (receiveBuffer.empty()) // buffer is empty
|
if (receiveBuffer.empty()) // buffer is empty
|
||||||
{
|
{
|
||||||
platform::getHandler()->wait();
|
m_socket->waitForRead();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,9 @@ public:
|
|||||||
|
|
||||||
enum Status
|
enum Status
|
||||||
{
|
{
|
||||||
STATUS_WOULDBLOCK = 0x1 /**< The receive operation would block. */
|
STATUS_WOULDBLOCK = 0xf, /**< The operation would block. Retry later. */
|
||||||
|
STATUS_WANT_READ = 0x1, /**< The socket wants to read data, retry when data is available. */
|
||||||
|
STATUS_WANT_WRITE = 0x2 /**< The socket wants to write data, retry when data can be written. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -74,6 +76,25 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool isConnected() const = 0;
|
virtual bool isConnected() const = 0;
|
||||||
|
|
||||||
|
/** Block until new data is available for reading. The function will
|
||||||
|
* timeout after msecs milliseconds.
|
||||||
|
*
|
||||||
|
* @param timeout maximum wait time, in milliseconds (default is 30000);
|
||||||
|
* resolution is 10ms
|
||||||
|
* @return true if data is available, or false if the operation timed out
|
||||||
|
*/
|
||||||
|
virtual bool waitForRead(const int msecs = 30000) = 0;
|
||||||
|
|
||||||
|
/** Block until pending data has been written and new data can be written.
|
||||||
|
* The function will timeout after msecs milliseconds.
|
||||||
|
*
|
||||||
|
* @param timeout maximum wait time, in milliseconds (default is 30000);
|
||||||
|
* resolution is 10ms
|
||||||
|
* @return true if new data can be written immediately, or false if the
|
||||||
|
* operation timed out
|
||||||
|
*/
|
||||||
|
virtual bool waitForWrite(const int msecs = 30000) = 0;
|
||||||
|
|
||||||
/** Receive text data from the socket.
|
/** Receive text data from the socket.
|
||||||
*
|
*
|
||||||
* @param buffer buffer in which to write received data
|
* @param buffer buffer in which to write received data
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
#include <gnutls/gnutls.h>
|
#include <gnutls/gnutls.h>
|
||||||
#include <gnutls/x509.h>
|
#include <gnutls/x509.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include "vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp"
|
#include "vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp"
|
||||||
#include "vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp"
|
#include "vmime/net/tls/gnutls/TLSSession_GnuTLS.hpp"
|
||||||
|
|
||||||
@ -57,7 +59,7 @@ shared_ptr <TLSSocket> TLSSocket::wrap(shared_ptr <TLSSession> session, shared_p
|
|||||||
|
|
||||||
TLSSocket_GnuTLS::TLSSocket_GnuTLS(shared_ptr <TLSSession_GnuTLS> session, shared_ptr <socket> sok)
|
TLSSocket_GnuTLS::TLSSocket_GnuTLS(shared_ptr <TLSSession_GnuTLS> session, shared_ptr <socket> sok)
|
||||||
: m_session(session), m_wrapped(sok), m_connected(false),
|
: m_session(session), m_wrapped(sok), m_connected(false),
|
||||||
m_handshaking(false), m_ex(NULL), m_status(0)
|
m_ex(NULL), m_status(0)
|
||||||
{
|
{
|
||||||
gnutls_transport_set_ptr(*m_session->m_gnutlsSession, this);
|
gnutls_transport_set_ptr(*m_session->m_gnutlsSession, this);
|
||||||
|
|
||||||
@ -144,6 +146,18 @@ shared_ptr <timeoutHandler> TLSSocket_GnuTLS::getTimeoutHandler()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool TLSSocket_GnuTLS::waitForRead(const int msecs)
|
||||||
|
{
|
||||||
|
return m_wrapped->waitForRead(msecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool TLSSocket_GnuTLS::waitForWrite(const int msecs)
|
||||||
|
{
|
||||||
|
return m_wrapped->waitForWrite(msecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void TLSSocket_GnuTLS::receive(string& buffer)
|
void TLSSocket_GnuTLS::receive(string& buffer)
|
||||||
{
|
{
|
||||||
const size_t size = receiveRaw(m_buffer, sizeof(m_buffer));
|
const size_t size = receiveRaw(m_buffer, sizeof(m_buffer));
|
||||||
@ -165,7 +179,7 @@ void TLSSocket_GnuTLS::send(const char* str)
|
|||||||
|
|
||||||
size_t TLSSocket_GnuTLS::receiveRaw(byte_t* buffer, const size_t count)
|
size_t TLSSocket_GnuTLS::receiveRaw(byte_t* buffer, const size_t count)
|
||||||
{
|
{
|
||||||
m_status &= ~STATUS_WOULDBLOCK;
|
m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ);
|
||||||
|
|
||||||
const ssize_t ret = gnutls_record_recv
|
const ssize_t ret = gnutls_record_recv
|
||||||
(*m_session->m_gnutlsSession,
|
(*m_session->m_gnutlsSession,
|
||||||
@ -178,7 +192,11 @@ size_t TLSSocket_GnuTLS::receiveRaw(byte_t* buffer, const size_t count)
|
|||||||
{
|
{
|
||||||
if (ret == GNUTLS_E_AGAIN)
|
if (ret == GNUTLS_E_AGAIN)
|
||||||
{
|
{
|
||||||
m_status |= STATUS_WOULDBLOCK;
|
if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0)
|
||||||
|
m_status |= STATUS_WANT_READ;
|
||||||
|
else
|
||||||
|
m_status |= STATUS_WANT_WRITE;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +209,7 @@ size_t TLSSocket_GnuTLS::receiveRaw(byte_t* buffer, const size_t count)
|
|||||||
|
|
||||||
void TLSSocket_GnuTLS::sendRaw(const byte_t* buffer, const size_t count)
|
void TLSSocket_GnuTLS::sendRaw(const byte_t* buffer, const size_t count)
|
||||||
{
|
{
|
||||||
m_status &= ~STATUS_WOULDBLOCK;
|
m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ);
|
||||||
|
|
||||||
for (size_t size = count ; size > 0 ; )
|
for (size_t size = count ; size > 0 ; )
|
||||||
{
|
{
|
||||||
@ -206,7 +224,11 @@ void TLSSocket_GnuTLS::sendRaw(const byte_t* buffer, const size_t count)
|
|||||||
{
|
{
|
||||||
if (ret == GNUTLS_E_AGAIN)
|
if (ret == GNUTLS_E_AGAIN)
|
||||||
{
|
{
|
||||||
platform::getHandler()->wait();
|
if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0)
|
||||||
|
m_wrapped->waitForRead();
|
||||||
|
else
|
||||||
|
m_wrapped->waitForWrite();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,6 +245,8 @@ void TLSSocket_GnuTLS::sendRaw(const byte_t* buffer, const size_t count)
|
|||||||
|
|
||||||
size_t TLSSocket_GnuTLS::sendRawNonBlocking(const byte_t* buffer, const size_t count)
|
size_t TLSSocket_GnuTLS::sendRawNonBlocking(const byte_t* buffer, const size_t count)
|
||||||
{
|
{
|
||||||
|
m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ);
|
||||||
|
|
||||||
ssize_t ret = gnutls_record_send
|
ssize_t ret = gnutls_record_send
|
||||||
(*m_session->m_gnutlsSession,
|
(*m_session->m_gnutlsSession,
|
||||||
buffer, static_cast <size_t>(count));
|
buffer, static_cast <size_t>(count));
|
||||||
@ -234,7 +258,11 @@ size_t TLSSocket_GnuTLS::sendRawNonBlocking(const byte_t* buffer, const size_t c
|
|||||||
{
|
{
|
||||||
if (ret == GNUTLS_E_AGAIN)
|
if (ret == GNUTLS_E_AGAIN)
|
||||||
{
|
{
|
||||||
m_status |= STATUS_WOULDBLOCK;
|
if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0)
|
||||||
|
m_status |= STATUS_WANT_READ;
|
||||||
|
else
|
||||||
|
m_status |= STATUS_WANT_WRITE;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,8 +287,6 @@ void TLSSocket_GnuTLS::handshake()
|
|||||||
toHandler->resetTimeOut();
|
toHandler->resetTimeOut();
|
||||||
|
|
||||||
// Start handshaking process
|
// Start handshaking process
|
||||||
m_handshaking = true;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
@ -272,11 +298,17 @@ void TLSSocket_GnuTLS::handshake()
|
|||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
if (ret == GNUTLS_E_AGAIN ||
|
if (ret == GNUTLS_E_AGAIN)
|
||||||
ret == GNUTLS_E_INTERRUPTED)
|
{
|
||||||
|
if (gnutls_record_get_direction(*m_session->m_gnutlsSession) == 0)
|
||||||
|
m_wrapped->waitForRead();
|
||||||
|
else
|
||||||
|
m_wrapped->waitForWrite();
|
||||||
|
}
|
||||||
|
else if (ret == GNUTLS_E_INTERRUPTED)
|
||||||
{
|
{
|
||||||
// Non-fatal error
|
// Non-fatal error
|
||||||
platform::getHandler()->wait();
|
m_wrapped->waitForRead();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -292,12 +324,9 @@ void TLSSocket_GnuTLS::handshake()
|
|||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
m_handshaking = false;
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_handshaking = false;
|
|
||||||
|
|
||||||
// Verify server's certificate(s)
|
// Verify server's certificate(s)
|
||||||
shared_ptr <security::cert::certificateChain> certs = getPeerCertificates();
|
shared_ptr <security::cert::certificateChain> certs = getPeerCertificates();
|
||||||
|
|
||||||
@ -321,14 +350,21 @@ ssize_t TLSSocket_GnuTLS::gnutlsPushFunc
|
|||||||
(sok->m_wrapped->sendRawNonBlocking
|
(sok->m_wrapped->sendRawNonBlocking
|
||||||
(reinterpret_cast <const byte_t*>(data), len));
|
(reinterpret_cast <const byte_t*>(data), len));
|
||||||
|
|
||||||
if (ret == 0 && sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK)
|
if (ret == 0)
|
||||||
return GNUTLS_E_AGAIN;
|
{
|
||||||
|
if (sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK)
|
||||||
|
gnutls_transport_set_errno(*sok->m_session->m_gnutlsSession, EAGAIN);
|
||||||
|
else
|
||||||
|
gnutls_transport_set_errno(*sok->m_session->m_gnutlsSession, 0);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (exception& e)
|
catch (exception& e)
|
||||||
{
|
{
|
||||||
// Workaround for bad behaviour when throwing C++ exceptions
|
// Workaround for non-portable behaviour when throwing C++ exceptions
|
||||||
// from C functions (GNU TLS)
|
// from C functions (GNU TLS)
|
||||||
sok->m_ex = e.clone();
|
sok->m_ex = e.clone();
|
||||||
return -1;
|
return -1;
|
||||||
@ -342,55 +378,26 @@ ssize_t TLSSocket_GnuTLS::gnutlsPullFunc
|
|||||||
TLSSocket_GnuTLS* sok = reinterpret_cast <TLSSocket_GnuTLS*>(trspt);
|
TLSSocket_GnuTLS* sok = reinterpret_cast <TLSSocket_GnuTLS*>(trspt);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
|
||||||
// Workaround for cross-platform asynchronous handshaking:
|
|
||||||
// gnutls_handshake() only returns GNUTLS_E_AGAIN if recv()
|
|
||||||
// returns -1 and errno is set to EGAIN...
|
|
||||||
if (sok->m_handshaking)
|
|
||||||
{
|
|
||||||
shared_ptr <timeoutHandler> toHandler = sok->m_wrapped->getTimeoutHandler();
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
const ssize_t ret = static_cast <ssize_t>
|
|
||||||
(sok->m_wrapped->receiveRaw
|
|
||||||
(reinterpret_cast <byte_t*>(data), len));
|
|
||||||
|
|
||||||
if (ret == 0)
|
|
||||||
{
|
|
||||||
// No data available yet
|
|
||||||
platform::getHandler()->wait();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the time-out delay is elapsed
|
|
||||||
if (toHandler && toHandler->isTimeOut())
|
|
||||||
{
|
|
||||||
if (!toHandler->handleTimeOut())
|
|
||||||
throw exceptions::operation_timed_out();
|
|
||||||
|
|
||||||
toHandler->resetTimeOut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
const ssize_t n = static_cast <ssize_t>
|
const ssize_t n = static_cast <ssize_t>
|
||||||
(sok->m_wrapped->receiveRaw
|
(sok->m_wrapped->receiveRaw
|
||||||
(reinterpret_cast <byte_t*>(data), len));
|
(reinterpret_cast <byte_t*>(data), len));
|
||||||
|
|
||||||
if (n == 0 && sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK)
|
if (n == 0)
|
||||||
return GNUTLS_E_AGAIN;
|
{
|
||||||
|
if (sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK)
|
||||||
|
gnutls_transport_set_errno(*sok->m_session->m_gnutlsSession, EAGAIN);
|
||||||
|
else
|
||||||
|
gnutls_transport_set_errno(*sok->m_session->m_gnutlsSession, 0);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (exception& e)
|
catch (exception& e)
|
||||||
{
|
{
|
||||||
// Workaround for bad behaviour when throwing C++ exceptions
|
// Workaround for non-portable behaviour when throwing C++ exceptions
|
||||||
// from C functions (GNU TLS)
|
// from C functions (GNU TLS)
|
||||||
sok->m_ex = e.clone();
|
sok->m_ex = e.clone();
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -63,6 +63,9 @@ public:
|
|||||||
void disconnect();
|
void disconnect();
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
|
|
||||||
|
bool waitForRead(const int msecs = 30000);
|
||||||
|
bool waitForWrite(const int msecs = 30000);
|
||||||
|
|
||||||
void receive(string& buffer);
|
void receive(string& buffer);
|
||||||
size_t receiveRaw(byte_t* buffer, const size_t count);
|
size_t receiveRaw(byte_t* buffer, const size_t count);
|
||||||
|
|
||||||
@ -100,8 +103,6 @@ private:
|
|||||||
|
|
||||||
byte_t m_buffer[65536];
|
byte_t m_buffer[65536];
|
||||||
|
|
||||||
bool m_handshaking;
|
|
||||||
|
|
||||||
exception* m_ex;
|
exception* m_ex;
|
||||||
|
|
||||||
unsigned int m_status;
|
unsigned int m_status;
|
||||||
|
@ -113,7 +113,7 @@ void TLSSocket_OpenSSL::createSSLHandle()
|
|||||||
|
|
||||||
SSL_set_bio(m_ssl, sockBio, sockBio);
|
SSL_set_bio(m_ssl, sockBio, sockBio);
|
||||||
SSL_set_connect_state(m_ssl);
|
SSL_set_connect_state(m_ssl);
|
||||||
SSL_set_mode(m_ssl, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
SSL_set_mode(m_ssl, SSL_MODE_AUTO_RETRY | SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -193,6 +193,18 @@ shared_ptr <timeoutHandler> TLSSocket_OpenSSL::getTimeoutHandler()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool TLSSocket_OpenSSL::waitForRead(const int msecs)
|
||||||
|
{
|
||||||
|
return m_wrapped->waitForRead(msecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool TLSSocket_OpenSSL::waitForWrite(const int msecs)
|
||||||
|
{
|
||||||
|
return m_wrapped->waitForWrite(msecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void TLSSocket_OpenSSL::receive(string& buffer)
|
void TLSSocket_OpenSSL::receive(string& buffer)
|
||||||
{
|
{
|
||||||
const size_t size = receiveRaw(m_buffer, sizeof(m_buffer));
|
const size_t size = receiveRaw(m_buffer, sizeof(m_buffer));
|
||||||
@ -221,7 +233,7 @@ size_t TLSSocket_OpenSSL::receiveRaw(byte_t* buffer, const size_t count)
|
|||||||
if (!m_ssl)
|
if (!m_ssl)
|
||||||
throw exceptions::socket_not_connected_exception();
|
throw exceptions::socket_not_connected_exception();
|
||||||
|
|
||||||
m_status &= ~STATUS_WOULDBLOCK;
|
m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ);
|
||||||
|
|
||||||
int rc = SSL_read(m_ssl, buffer, static_cast <int>(count));
|
int rc = SSL_read(m_ssl, buffer, static_cast <int>(count));
|
||||||
|
|
||||||
@ -232,9 +244,14 @@ size_t TLSSocket_OpenSSL::receiveRaw(byte_t* buffer, const size_t count)
|
|||||||
{
|
{
|
||||||
int error = SSL_get_error(m_ssl, rc);
|
int error = SSL_get_error(m_ssl, rc);
|
||||||
|
|
||||||
if (error == SSL_ERROR_WANT_WRITE || error == SSL_ERROR_WANT_READ)
|
if (error == SSL_ERROR_WANT_WRITE)
|
||||||
{
|
{
|
||||||
m_status |= STATUS_WOULDBLOCK;
|
m_status |= STATUS_WANT_WRITE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (error == SSL_ERROR_WANT_READ)
|
||||||
|
{
|
||||||
|
m_status |= STATUS_WANT_READ;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +267,7 @@ void TLSSocket_OpenSSL::sendRaw(const byte_t* buffer, const size_t count)
|
|||||||
if (!m_ssl)
|
if (!m_ssl)
|
||||||
throw exceptions::socket_not_connected_exception();
|
throw exceptions::socket_not_connected_exception();
|
||||||
|
|
||||||
m_status &= ~STATUS_WOULDBLOCK;
|
m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ);
|
||||||
|
|
||||||
for (size_t size = count ; size > 0 ; )
|
for (size_t size = count ; size > 0 ; )
|
||||||
{
|
{
|
||||||
@ -260,9 +277,14 @@ void TLSSocket_OpenSSL::sendRaw(const byte_t* buffer, const size_t count)
|
|||||||
{
|
{
|
||||||
int error = SSL_get_error(m_ssl, rc);
|
int error = SSL_get_error(m_ssl, rc);
|
||||||
|
|
||||||
if (error == SSL_ERROR_WANT_WRITE || error == SSL_ERROR_WANT_READ)
|
if (error == SSL_ERROR_WANT_READ)
|
||||||
{
|
{
|
||||||
platform::getHandler()->wait();
|
m_wrapped->waitForRead();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (error == SSL_ERROR_WANT_WRITE)
|
||||||
|
{
|
||||||
|
m_wrapped->waitForWrite();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +304,7 @@ size_t TLSSocket_OpenSSL::sendRawNonBlocking(const byte_t* buffer, const size_t
|
|||||||
if (!m_ssl)
|
if (!m_ssl)
|
||||||
throw exceptions::socket_not_connected_exception();
|
throw exceptions::socket_not_connected_exception();
|
||||||
|
|
||||||
m_status &= ~STATUS_WOULDBLOCK;
|
m_status &= ~(STATUS_WANT_WRITE | STATUS_WANT_READ);
|
||||||
|
|
||||||
int rc = SSL_write(m_ssl, buffer, static_cast <int>(count));
|
int rc = SSL_write(m_ssl, buffer, static_cast <int>(count));
|
||||||
|
|
||||||
@ -293,9 +315,14 @@ size_t TLSSocket_OpenSSL::sendRawNonBlocking(const byte_t* buffer, const size_t
|
|||||||
{
|
{
|
||||||
int error = SSL_get_error(m_ssl, rc);
|
int error = SSL_get_error(m_ssl, rc);
|
||||||
|
|
||||||
if (error == SSL_ERROR_WANT_WRITE || error == SSL_ERROR_WANT_READ)
|
if (error == SSL_ERROR_WANT_WRITE)
|
||||||
{
|
{
|
||||||
m_status |= STATUS_WOULDBLOCK;
|
m_status |= STATUS_WANT_WRITE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (error == SSL_ERROR_WANT_READ)
|
||||||
|
{
|
||||||
|
m_status |= STATUS_WANT_READ;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,15 +355,12 @@ void TLSSocket_OpenSSL::handshake()
|
|||||||
{
|
{
|
||||||
const int err = SSL_get_error(m_ssl, rc);
|
const int err = SSL_get_error(m_ssl, rc);
|
||||||
|
|
||||||
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
|
if (err == SSL_ERROR_WANT_READ)
|
||||||
{
|
m_wrapped->waitForRead();
|
||||||
// No data available yet
|
else if (err == SSL_ERROR_WANT_WRITE)
|
||||||
platform::getHandler()->wait();
|
m_wrapped->waitForWrite();
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
handleError(rc);
|
handleError(rc);
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether the time-out delay is elapsed
|
// Check whether the time-out delay is elapsed
|
||||||
if (toHandler && toHandler->isTimeOut())
|
if (toHandler && toHandler->isTimeOut())
|
||||||
|
@ -67,6 +67,9 @@ public:
|
|||||||
void disconnect();
|
void disconnect();
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
|
|
||||||
|
bool waitForRead(const int msecs = 30000);
|
||||||
|
bool waitForWrite(const int msecs = 30000);
|
||||||
|
|
||||||
void receive(string& buffer);
|
void receive(string& buffer);
|
||||||
size_t receiveRaw(byte_t* buffer, const size_t count);
|
size_t receiveRaw(byte_t* buffer, const size_t count);
|
||||||
|
|
||||||
@ -115,7 +118,7 @@ private:
|
|||||||
unsigned long m_status;
|
unsigned long m_status;
|
||||||
|
|
||||||
// Last exception thrown from C BIO functions
|
// Last exception thrown from C BIO functions
|
||||||
std::auto_ptr <std::exception> m_ex;
|
std::auto_ptr <exception> m_ex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -104,13 +104,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual const charset getLocalCharset() const = 0;
|
virtual const charset getLocalCharset() const = 0;
|
||||||
|
|
||||||
/** This function is called when VMime library is waiting for
|
|
||||||
* something (for example, it is called when there is no data
|
|
||||||
* available in a socket). On POSIX-compliant systems, a
|
|
||||||
* simple call to sched_yield() should suffice.
|
|
||||||
*/
|
|
||||||
virtual void wait() const = 0;
|
|
||||||
|
|
||||||
#if VMIME_HAVE_MESSAGING_FEATURES
|
#if VMIME_HAVE_MESSAGING_FEATURES
|
||||||
/** Return a pointer to the default socket factory for
|
/** Return a pointer to the default socket factory for
|
||||||
* this platform.
|
* this platform.
|
||||||
|
@ -269,24 +269,6 @@ shared_ptr <vmime::utility::childProcessFactory> posixHandler::getChildProcessFa
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void posixHandler::wait() const
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
#ifdef _POSIX_PRIORITY_SCHEDULING
|
|
||||||
::sched_yield();
|
|
||||||
#else
|
|
||||||
::sleep(1);
|
|
||||||
#endif // _POSIX_PRIORITY_SCHEDULING
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct timespec ts;
|
|
||||||
ts.tv_sec = 0;
|
|
||||||
ts.tv_nsec = 500000; // 500 microseconds
|
|
||||||
|
|
||||||
nanosleep(&ts, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void posixHandler::generateRandomBytes(unsigned char* buffer, const unsigned int count)
|
void posixHandler::generateRandomBytes(unsigned char* buffer, const unsigned int count)
|
||||||
{
|
{
|
||||||
int fd = open("/dev/urandom", O_RDONLY);
|
int fd = open("/dev/urandom", O_RDONLY);
|
||||||
|
@ -76,8 +76,6 @@ public:
|
|||||||
shared_ptr <vmime::utility::childProcessFactory> getChildProcessFactory();
|
shared_ptr <vmime::utility::childProcessFactory> getChildProcessFactory();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void wait() const;
|
|
||||||
|
|
||||||
void generateRandomBytes(unsigned char* buffer, const unsigned int count);
|
void generateRandomBytes(unsigned char* buffer, const unsigned int count);
|
||||||
|
|
||||||
shared_ptr <utility::sync::criticalSection> createCriticalSection();
|
shared_ptr <utility::sync::criticalSection> createCriticalSection();
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/time.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -111,13 +112,23 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
// Connect to host
|
// Connect to host
|
||||||
int sock = -1;
|
int sock = -1;
|
||||||
struct ::addrinfo* res = res0;
|
struct ::addrinfo* res = res0;
|
||||||
|
int connectErrno = 0;
|
||||||
|
|
||||||
for ( ; sock == -1 && res != NULL ; res = res->ai_next)
|
if (m_timeoutHandler != NULL)
|
||||||
|
m_timeoutHandler->resetTimeOut();
|
||||||
|
|
||||||
|
for ( ; sock == -1 && res != NULL ; res = res->ai_next, connectErrno = ETIMEDOUT)
|
||||||
{
|
{
|
||||||
|
if (res->ai_family != AF_INET && res->ai_family != AF_INET6)
|
||||||
|
continue;
|
||||||
|
|
||||||
sock = ::socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
sock = ::socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||||
|
|
||||||
if (sock < 0)
|
if (sock < 0)
|
||||||
|
{
|
||||||
|
connectErrno = errno;
|
||||||
continue; // try next
|
continue; // try next
|
||||||
|
}
|
||||||
|
|
||||||
if (m_timeoutHandler != NULL)
|
if (m_timeoutHandler != NULL)
|
||||||
{
|
{
|
||||||
@ -142,37 +153,51 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
|
connectErrno = errno;
|
||||||
::close(sock);
|
::close(sock);
|
||||||
sock = -1;
|
sock = -1;
|
||||||
continue; // try next
|
continue; // try next
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for socket to be connected.
|
// Wait for socket to be connected.
|
||||||
// We will check for time out every second.
|
bool connected = false;
|
||||||
|
|
||||||
|
const int selectTimeout = 1000; // select() timeout (ms)
|
||||||
|
const int tryNextTimeout = 5000; // maximum time before trying next (ms)
|
||||||
|
|
||||||
|
timeval startTime = { 0, 0 };
|
||||||
|
gettimeofday(&startTime, /* timezone */ NULL);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
struct timeval tm;
|
||||||
|
tm.tv_sec = selectTimeout / 1000;
|
||||||
|
tm.tv_usec = selectTimeout % 1000;
|
||||||
|
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(sock, &fds);
|
FD_SET(sock, &fds);
|
||||||
|
|
||||||
fd_set fdsError;
|
const int ret = select(sock + 1, NULL, &fds, NULL, &tm);
|
||||||
FD_ZERO(&fdsError);
|
|
||||||
FD_SET(sock, &fdsError);
|
|
||||||
|
|
||||||
struct timeval tm;
|
|
||||||
tm.tv_sec = 1;
|
|
||||||
tm.tv_usec = 0;
|
|
||||||
|
|
||||||
m_timeoutHandler->resetTimeOut();
|
|
||||||
|
|
||||||
bool connected = false;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
const int ret = select(sock + 1, NULL, &fds, &fdsError, &tm);
|
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
{
|
{
|
||||||
|
int error = 0;
|
||||||
|
socklen_t len = sizeof(error);
|
||||||
|
|
||||||
|
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
|
||||||
|
{
|
||||||
|
connectErrno = errno;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (error != 0)
|
||||||
|
connectErrno = error;
|
||||||
|
else
|
||||||
connected = true;
|
connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Error
|
// Error
|
||||||
@ -181,10 +206,11 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
{
|
{
|
||||||
// Cancel connection
|
// Cancel connection
|
||||||
|
connectErrno = errno;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 1-second timeout
|
// Check for timeout
|
||||||
else if (ret == 0)
|
else if (ret == 0)
|
||||||
{
|
{
|
||||||
if (m_timeoutHandler->isTimeOut())
|
if (m_timeoutHandler->isTimeOut())
|
||||||
@ -192,6 +218,7 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
if (!m_timeoutHandler->handleTimeOut())
|
if (!m_timeoutHandler->handleTimeOut())
|
||||||
{
|
{
|
||||||
// Cancel connection
|
// Cancel connection
|
||||||
|
connectErrno = ETIMEDOUT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -206,7 +233,15 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::sched_yield();
|
timeval curTime = { 0, 0 };
|
||||||
|
gettimeofday(&curTime, /* timezone */ NULL);
|
||||||
|
|
||||||
|
if (res->ai_next != NULL &&
|
||||||
|
curTime.tv_usec - startTime.tv_usec >= tryNextTimeout * 1000)
|
||||||
|
{
|
||||||
|
connectErrno = ETIMEDOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
} while (true);
|
} while (true);
|
||||||
|
|
||||||
@ -219,11 +254,17 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Connection successful
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (::connect(sock, res->ai_addr, res->ai_addrlen) < 0)
|
if (::connect(sock, res->ai_addr, res->ai_addrlen) < 0)
|
||||||
{
|
{
|
||||||
|
connectErrno = errno;
|
||||||
::close(sock);
|
::close(sock);
|
||||||
sock = -1;
|
sock = -1;
|
||||||
continue; // try next
|
continue; // try next
|
||||||
@ -237,7 +278,7 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
throwSocketError(errno);
|
throwSocketError(connectErrno);
|
||||||
}
|
}
|
||||||
catch (exceptions::socket_exception& e)
|
catch (exceptions::socket_exception& e)
|
||||||
{
|
{
|
||||||
@ -434,6 +475,65 @@ size_t posixSocket::getBlockSize() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool posixSocket::waitForData(const bool read, const bool write, const int msecs)
|
||||||
|
{
|
||||||
|
for (int i = 0 ; i <= msecs / 10 ; ++i)
|
||||||
|
{
|
||||||
|
// Check whether data is available
|
||||||
|
fd_set fds;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(m_desc, &fds);
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = 0;
|
||||||
|
tv.tv_usec = 10000; // 10 ms
|
||||||
|
|
||||||
|
ssize_t ret = ::select(m_desc + 1, read ? &fds : NULL, write ? &fds : NULL, NULL, &tv);
|
||||||
|
|
||||||
|
if (ret <= 0)
|
||||||
|
{
|
||||||
|
if (ret < 0 && !IS_EAGAIN(errno))
|
||||||
|
throwSocketError(errno);
|
||||||
|
|
||||||
|
// No data available at this time
|
||||||
|
// Check if we are timed out
|
||||||
|
if (m_timeoutHandler &&
|
||||||
|
m_timeoutHandler->isTimeOut())
|
||||||
|
{
|
||||||
|
if (!m_timeoutHandler->handleTimeOut())
|
||||||
|
{
|
||||||
|
// Server did not react within timeout delay
|
||||||
|
throw exceptions::operation_timed_out();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Reset timeout
|
||||||
|
m_timeoutHandler->resetTimeOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ret > 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // time out
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool posixSocket::waitForRead(const int msecs)
|
||||||
|
{
|
||||||
|
return waitForData(/* read */ true, /* write */ false, msecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool posixSocket::waitForWrite(const int msecs)
|
||||||
|
{
|
||||||
|
return waitForData(/* read */ false, /* write */ true, msecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void posixSocket::receive(vmime::string& buffer)
|
void posixSocket::receive(vmime::string& buffer)
|
||||||
{
|
{
|
||||||
const size_t size = receiveRaw(m_buffer, sizeof(m_buffer));
|
const size_t size = receiveRaw(m_buffer, sizeof(m_buffer));
|
||||||
@ -446,38 +546,8 @@ size_t posixSocket::receiveRaw(byte_t* buffer, const size_t count)
|
|||||||
m_status &= ~STATUS_WOULDBLOCK;
|
m_status &= ~STATUS_WOULDBLOCK;
|
||||||
|
|
||||||
// Check whether data is available
|
// Check whether data is available
|
||||||
fd_set fds;
|
if (!waitForRead(50 /* msecs */))
|
||||||
FD_ZERO(&fds);
|
|
||||||
FD_SET(m_desc, &fds);
|
|
||||||
|
|
||||||
struct timeval tv;
|
|
||||||
tv.tv_sec = 1;
|
|
||||||
tv.tv_usec = 0;
|
|
||||||
|
|
||||||
ssize_t ret = ::select(m_desc + 1, &fds, NULL, NULL, &tv);
|
|
||||||
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
{
|
||||||
if (!IS_EAGAIN(errno))
|
|
||||||
throwSocketError(errno);
|
|
||||||
|
|
||||||
// No data available at this time
|
|
||||||
// Check if we are timed out
|
|
||||||
if (m_timeoutHandler &&
|
|
||||||
m_timeoutHandler->isTimeOut())
|
|
||||||
{
|
|
||||||
if (!m_timeoutHandler->handleTimeOut())
|
|
||||||
{
|
|
||||||
// Server did not react within timeout delay
|
|
||||||
throwSocketError(errno);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Reset timeout
|
|
||||||
m_timeoutHandler->resetTimeOut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_status |= STATUS_WOULDBLOCK;
|
m_status |= STATUS_WOULDBLOCK;
|
||||||
|
|
||||||
// Continue waiting for data
|
// Continue waiting for data
|
||||||
@ -485,7 +555,7 @@ size_t posixSocket::receiveRaw(byte_t* buffer, const size_t count)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read available data
|
// Read available data
|
||||||
ret = ::recv(m_desc, buffer, count, 0);
|
ssize_t ret = ::recv(m_desc, buffer, count, 0);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
@ -556,7 +626,7 @@ void posixSocket::sendRaw(const byte_t* buffer, const size_t count)
|
|||||||
if (ret < 0 && !IS_EAGAIN(errno))
|
if (ret < 0 && !IS_EAGAIN(errno))
|
||||||
throwSocketError(errno);
|
throwSocketError(errno);
|
||||||
|
|
||||||
platform::getHandler()->wait();
|
waitForWrite(50 /* msecs */);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -589,7 +659,7 @@ size_t posixSocket::sendRawNonBlocking(const byte_t* buffer, const size_t count)
|
|||||||
if (!m_timeoutHandler->handleTimeOut())
|
if (!m_timeoutHandler->handleTimeOut())
|
||||||
{
|
{
|
||||||
// Could not send data within timeout delay
|
// Could not send data within timeout delay
|
||||||
throwSocketError(errno);
|
throw exceptions::operation_timed_out();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -50,6 +50,9 @@ public:
|
|||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
void disconnect();
|
void disconnect();
|
||||||
|
|
||||||
|
bool waitForRead(const int msecs = 30000);
|
||||||
|
bool waitForWrite(const int msecs = 30000);
|
||||||
|
|
||||||
void receive(vmime::string& buffer);
|
void receive(vmime::string& buffer);
|
||||||
size_t receiveRaw(byte_t* buffer, const size_t count);
|
size_t receiveRaw(byte_t* buffer, const size_t count);
|
||||||
|
|
||||||
@ -69,6 +72,8 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
bool waitForData(const bool read, const bool write, const int msecs);
|
||||||
|
|
||||||
static void throwSocketError(const int err);
|
static void throwSocketError(const int err);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -299,12 +299,6 @@ shared_ptr <vmime::utility::childProcessFactory> windowsHandler::getChildProcess
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void windowsHandler::wait() const
|
|
||||||
{
|
|
||||||
::Sleep(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void windowsHandler::generateRandomBytes(unsigned char* buffer, const unsigned int count)
|
void windowsHandler::generateRandomBytes(unsigned char* buffer, const unsigned int count)
|
||||||
{
|
{
|
||||||
HCRYPTPROV cryptProvider = 0;
|
HCRYPTPROV cryptProvider = 0;
|
||||||
|
@ -75,8 +75,6 @@ public:
|
|||||||
shared_ptr <vmime::utility::childProcessFactory> getChildProcessFactory();
|
shared_ptr <vmime::utility::childProcessFactory> getChildProcessFactory();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void wait() const;
|
|
||||||
|
|
||||||
void generateRandomBytes(unsigned char* buffer, const unsigned int count);
|
void generateRandomBytes(unsigned char* buffer, const unsigned int count);
|
||||||
|
|
||||||
shared_ptr <utility::sync::criticalSection> createCriticalSection();
|
shared_ptr <utility::sync::criticalSection> createCriticalSection();
|
||||||
|
@ -249,10 +249,7 @@ size_t windowsSocket::receiveRaw(byte_t* buffer, const size_t count)
|
|||||||
m_status &= ~STATUS_WOULDBLOCK;
|
m_status &= ~STATUS_WOULDBLOCK;
|
||||||
|
|
||||||
// Check whether data is available
|
// Check whether data is available
|
||||||
bool timedout;
|
if (!waitForRead(50 /* msecs */))
|
||||||
waitForData(READ, timedout);
|
|
||||||
|
|
||||||
if (timedout)
|
|
||||||
{
|
{
|
||||||
// No data available at this time
|
// No data available at this time
|
||||||
// Check if we are timed out
|
// Check if we are timed out
|
||||||
@ -335,8 +332,7 @@ void windowsSocket::sendRaw(const byte_t* buffer, const size_t count)
|
|||||||
if (err != WSAEWOULDBLOCK)
|
if (err != WSAEWOULDBLOCK)
|
||||||
throwSocketError(err);
|
throwSocketError(err);
|
||||||
|
|
||||||
bool timedout;
|
waitForWrite(50 /* msecs */);
|
||||||
waitForData(WRITE, timedout);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -430,7 +426,9 @@ void windowsSocket::throwSocketError(const int err)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void windowsSocket::waitForData(const WaitOpType t, bool& timedOut)
|
bool windowsSocket::waitForData(const bool read, const bool write, const int msecs)
|
||||||
|
{
|
||||||
|
for (int i = 0 ; i <= msecs / 10 ; ++i)
|
||||||
{
|
{
|
||||||
// Check whether data is available
|
// Check whether data is available
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
@ -438,25 +436,52 @@ void windowsSocket::waitForData(const WaitOpType t, bool& timedOut)
|
|||||||
FD_SET(m_desc, &fds);
|
FD_SET(m_desc, &fds);
|
||||||
|
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
tv.tv_sec = 1;
|
tv.tv_sec = 0;
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 10000; // 10 ms
|
||||||
|
|
||||||
int ret;
|
ssize_t ret = ::select(m_desc + 1, read ? &fds : NULL, write ? &fds : NULL, NULL, &tv);
|
||||||
|
|
||||||
if (t & READ)
|
|
||||||
ret = ::select(m_desc + 1, &fds, NULL, NULL, &tv);
|
|
||||||
else if (t & WRITE)
|
|
||||||
ret = ::select(m_desc + 1, NULL, &fds, NULL, &tv);
|
|
||||||
else
|
|
||||||
ret = ::select(m_desc + 1, &fds, &fds, NULL, &tv);
|
|
||||||
|
|
||||||
timedOut = (ret == 0);
|
|
||||||
|
|
||||||
if (ret == SOCKET_ERROR)
|
if (ret == SOCKET_ERROR)
|
||||||
{
|
{
|
||||||
int err = WSAGetLastError();
|
int err = WSAGetLastError();
|
||||||
throwSocketError(err);
|
throwSocketError(err);
|
||||||
}
|
}
|
||||||
|
else if (ret > 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No data available at this time
|
||||||
|
// Check if we are timed out
|
||||||
|
if (m_timeoutHandler &&
|
||||||
|
m_timeoutHandler->isTimeOut())
|
||||||
|
{
|
||||||
|
if (!m_timeoutHandler->handleTimeOut())
|
||||||
|
{
|
||||||
|
// Server did not react within timeout delay
|
||||||
|
throw exceptions::operation_timed_out();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Reset timeout
|
||||||
|
m_timeoutHandler->resetTimeOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // time out
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool windowsSocket::waitForRead(const int msecs)
|
||||||
|
{
|
||||||
|
return waitForData(/* read */ true, /* write */ false, msecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool windowsSocket::waitForWrite(const int msecs = 30000)
|
||||||
|
{
|
||||||
|
return waitForData(/* read */ false, /* write */ true, msecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,6 +54,9 @@ public:
|
|||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
void disconnect();
|
void disconnect();
|
||||||
|
|
||||||
|
bool waitForRead(const int msecs = 30000);
|
||||||
|
bool waitForWrite(const int msecs = 30000);
|
||||||
|
|
||||||
void receive(vmime::string& buffer);
|
void receive(vmime::string& buffer);
|
||||||
size_t receiveRaw(byte_t* buffer, const size_t count);
|
size_t receiveRaw(byte_t* buffer, const size_t count);
|
||||||
|
|
||||||
@ -75,14 +78,7 @@ protected:
|
|||||||
|
|
||||||
void throwSocketError(const int err);
|
void throwSocketError(const int err);
|
||||||
|
|
||||||
enum WaitOpType
|
bool waitForData(const bool read, const bool write, const int msecs);
|
||||||
{
|
|
||||||
READ = 1,
|
|
||||||
WRITE = 2,
|
|
||||||
BOTH = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
void waitForData(const WaitOpType t, bool& timedOut);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -102,6 +102,18 @@ shared_ptr <net::timeoutHandler> SASLSocket::getTimeoutHandler()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SASLSocket::waitForRead(const int msecs)
|
||||||
|
{
|
||||||
|
return m_wrapped->waitForRead(msecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SASLSocket::waitForWrite(const int msecs)
|
||||||
|
{
|
||||||
|
return m_wrapped->waitForWrite(msecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SASLSocket::receive(string& buffer)
|
void SASLSocket::receive(string& buffer)
|
||||||
{
|
{
|
||||||
const size_t n = receiveRaw(m_recvBuffer, sizeof(m_recvBuffer));
|
const size_t n = receiveRaw(m_recvBuffer, sizeof(m_recvBuffer));
|
||||||
|
@ -58,6 +58,9 @@ public:
|
|||||||
|
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
|
|
||||||
|
bool waitForRead(const int msecs = 30000);
|
||||||
|
bool waitForWrite(const int msecs = 30000);
|
||||||
|
|
||||||
void receive(string& buffer);
|
void receive(string& buffer);
|
||||||
size_t receiveRaw(byte_t* buffer, const size_t count);
|
size_t receiveRaw(byte_t* buffer, const size_t count);
|
||||||
|
|
||||||
|
@ -85,6 +85,18 @@ vmime::shared_ptr <vmime::net::timeoutHandler> testSocket::getTimeoutHandler()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool testSocket::waitForRead(const int msecs)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool testSocket::waitForWrite(const int msecs)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void testSocket::receive(vmime::string& buffer)
|
void testSocket::receive(vmime::string& buffer)
|
||||||
{
|
{
|
||||||
buffer = m_inBuffer;
|
buffer = m_inBuffer;
|
||||||
|
@ -248,6 +248,9 @@ public:
|
|||||||
|
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
|
|
||||||
|
bool waitForWrite(const int msecs = 30000);
|
||||||
|
bool waitForRead(const int msecs = 30000);
|
||||||
|
|
||||||
void receive(vmime::string& buffer);
|
void receive(vmime::string& buffer);
|
||||||
void send(const vmime::string& buffer);
|
void send(const vmime::string& buffer);
|
||||||
void send(const char* str);
|
void send(const char* str);
|
||||||
|
Loading…
Reference in New Issue
Block a user