Cleaned up socket error handling.

This commit is contained in:
Gunnar Beutner 2012-05-26 20:00:03 +02:00
parent 4819449efb
commit 5dfb1cc4b4
12 changed files with 150 additions and 60 deletions

View File

@ -26,6 +26,7 @@ using namespace icinga;
*/ */
Exception::Exception(void) Exception::Exception(void)
{ {
m_Code = 0;
m_Message = NULL; m_Message = NULL;
} }
@ -36,6 +37,7 @@ Exception::Exception(void)
*/ */
Exception::Exception(const char *message) Exception::Exception(const char *message)
{ {
m_Code = 0;
m_Message = NULL; m_Message = NULL;
SetMessage(message); SetMessage(message);
} }
@ -48,6 +50,26 @@ Exception::~Exception(void) throw()
Memory::Free(m_Message); Memory::Free(m_Message);
} }
/**
* Retrieves the error code for the exception.
*
* @returns The error code.
*/
int Exception::GetCode(void) const
{
return m_Code;
}
/**
* Sets the error code for the exception.
*
* @param code The error code.
*/
void Exception::SetCode(int code)
{
m_Code = code;
}
/** /**
* Retrieves the description for the exception. * Retrieves the description for the exception.
* *

View File

@ -35,15 +35,18 @@ public:
Exception(const char *message); Exception(const char *message);
virtual ~Exception(void) throw(); virtual ~Exception(void) throw();
int GetCode(void) const;
const char *GetMessage(void) const; const char *GetMessage(void) const;
virtual const char *what(void) const throw(); virtual const char *what(void) const throw();
protected: protected:
void SetCode(int code);
void SetMessage(const char *message); void SetMessage(const char *message);
private: private:
char *m_Message; char *m_Message;
int m_Code;
}; };
#define DEFINE_EXCEPTION_CLASS(klass) \ #define DEFINE_EXCEPTION_CLASS(klass) \
@ -101,6 +104,7 @@ public:
{ {
string msg = message + ": " + FormatErrorCode(errorCode); string msg = message + ": " + FormatErrorCode(errorCode);
SetMessage(msg.c_str()); SetMessage(msg.c_str());
SetCode(errorCode);
} }
/** /**
@ -129,6 +133,7 @@ public:
{ {
string msg = message + ": " + FormatErrorCode(errorCode); string msg = message + ": " + FormatErrorCode(errorCode);
SetMessage(msg.c_str()); SetMessage(msg.c_str());
SetCode(errorCode);
} }
/** /**

View File

@ -86,6 +86,16 @@ public:
} }
} }
/**
* Checks whether there's at least one observer.
*
* @returns true if there are one or more observers, false otherwise.
*/
bool HasObservers(void) const
{
return !m_Observers.empty();
}
private: private:
vector<ObserverType> m_Observers; vector<ObserverType> m_Observers;
}; };

View File

@ -132,28 +132,53 @@ void Socket::CloseInternal(bool from_dtor)
} }
/** /**
* Handles a socket error by calling the OnError event. * Retrieves the last error that occured for the socket.
*
* @returns An error code.
*/ */
void Socket::HandleSocketError(void) int Socket::GetError(void) const
{ {
int opt; int opt;
socklen_t optlen = sizeof(opt); socklen_t optlen = sizeof(opt);
int rc = getsockopt(GetFD(), SOL_SOCKET, SO_ERROR, (char *)&opt, &optlen); int rc = getsockopt(GetFD(), SOL_SOCKET, SO_ERROR, (char *)&opt, &optlen);
if (rc >= 0 && opt != 0) { if (rc >= 0)
SocketErrorEventArgs sea; return opt;
sea.Code = opt;
#ifdef _WIN32
sea.Message = Win32Exception::FormatErrorCode(sea.Code);
#else /* _WIN32 */
sea.Message = PosixException::FormatErrorCode(sea.Code);
#endif /* _WIN32 */
OnError(sea);
}
Close(); return 0;
return; }
/**
* Retrieves the last socket error.
*
* @returns An error code.
*/
int Socket::GetLastSocketError(void)
{
#ifdef _WIN32
return WSAGetLastError();
#else /* _WIN32 */
return errno;
#endif /* _WIN32 */
}
/**
* Handles a socket error by calling the OnError event or throwing an exception
* when there are no observers for the OnError event.
*
* @param exception An exception.
*/
void Socket::HandleSocketError(const Exception& exception)
{
if (OnError.HasObservers()) {
SocketErrorEventArgs sea(exception);
OnError(sea);
Close();
} else {
throw exception;
}
} }
/** /**
@ -164,7 +189,8 @@ void Socket::HandleSocketError(void)
*/ */
int Socket::ExceptionEventHandler(const EventArgs&) int Socket::ExceptionEventHandler(const EventArgs&)
{ {
HandleSocketError(); HandleSocketError(SocketException(
"select() returned fd in except fdset", GetError()));
return 0; return 0;
} }
@ -218,7 +244,8 @@ string Socket::GetClientAddress(void)
socklen_t len = sizeof(sin); socklen_t len = sizeof(sin);
if (getsockname(GetFD(), (sockaddr *)&sin, &len) < 0) { if (getsockname(GetFD(), (sockaddr *)&sin, &len) < 0) {
HandleSocketError(); HandleSocketError(SocketException(
"getsockname() failed", GetError()));
return string(); return string();
} }
@ -237,10 +264,29 @@ string Socket::GetPeerAddress(void)
socklen_t len = sizeof(sin); socklen_t len = sizeof(sin);
if (getpeername(GetFD(), (sockaddr *)&sin, &len) < 0) { if (getpeername(GetFD(), (sockaddr *)&sin, &len) < 0) {
HandleSocketError(); HandleSocketError(SocketException(
"getpeername() failed", GetError()));
return string(); return string();
} }
return GetAddressFromSockaddr((sockaddr *)&sin, len); return GetAddressFromSockaddr((sockaddr *)&sin, len);
} }
/**
* Constructor for the SocketException class.
*
* @param message The error message.
* @param errorCode The error code.
*/
SocketException::SocketException(const string& message, int errorCode)
{
#ifdef _WIN32
string details = Win32Exception::FormatErrorCode(errorCode);
#else /* _WIN32 */
string details = PosixException::FormatErrorCode(errorCode);
#endif /* _WIN32 */
string msg = message + ": " + details;
SetMessage(msg.c_str());
}

View File

@ -29,8 +29,10 @@ namespace icinga {
*/ */
struct I2_BASE_API SocketErrorEventArgs : public EventArgs struct I2_BASE_API SocketErrorEventArgs : public EventArgs
{ {
int Code; /**< The error code. */ const Exception& Error;
string Message; /**< A message describing the error. */
SocketErrorEventArgs(const Exception& exception)
: Error(exception) { }
}; };
/** /**
@ -74,7 +76,10 @@ public:
protected: protected:
Socket(void); Socket(void);
void HandleSocketError(void); int GetError(void) const;
static int GetLastSocketError(void);
void HandleSocketError(const Exception& exception);
virtual void CloseInternal(bool from_dtor); virtual void CloseInternal(bool from_dtor);
private: private:
@ -85,6 +90,15 @@ private:
static string GetAddressFromSockaddr(sockaddr *address, socklen_t len); static string GetAddressFromSockaddr(sockaddr *address, socklen_t len);
}; };
/**
* A socket exception.
*/
class SocketException : public Exception
{
public:
SocketException(const string& message, int errorCode);
};
} }
#endif /* SOCKET_H */ #endif /* SOCKET_H */

View File

@ -76,8 +76,8 @@ void TcpClient::Connect(const string& node, const string& service)
int rc = getaddrinfo(node.c_str(), service.c_str(), &hints, &result); int rc = getaddrinfo(node.c_str(), service.c_str(), &hints, &result);
if (rc < 0) { if (rc < 0) {
HandleSocketError(); HandleSocketError(SocketException(
"getaddrinfo() failed", GetLastSocketError()));
return; return;
} }
@ -94,19 +94,23 @@ void TcpClient::Connect(const string& node, const string& service)
rc = connect(fd, info->ai_addr, info->ai_addrlen); rc = connect(fd, info->ai_addr, info->ai_addrlen);
#ifdef _WIN32 #ifdef _WIN32
if (rc < 0 && WSAGetLastError() != WSAEWOULDBLOCK) if (rc < 0 && WSAGetLastError() != WSAEWOULDBLOCK) {
#else /* _WIN32 */ #else /* _WIN32 */
if (rc < 0 && errno != EINPROGRESS) if (rc < 0 && errno != EINPROGRESS) {
#endif /* _WIN32 */ #endif /* _WIN32 */
closesocket(fd);
SetFD(INVALID_SOCKET);
continue; continue;
}
break; break;
} }
if (fd == INVALID_SOCKET)
HandleSocketError();
freeaddrinfo(result); freeaddrinfo(result);
if (fd == INVALID_SOCKET)
HandleSocketError(InvalidArgumentException());
} }
/** /**
@ -151,7 +155,7 @@ int TcpClient::ReadableEventHandler(const EventArgs&)
return 0; return 0;
if (rc <= 0) { if (rc <= 0) {
HandleSocketError(); HandleSocketError(SocketException("recv() failed", GetError()));
return 0; return 0;
} }
@ -177,7 +181,7 @@ int TcpClient::WritableEventHandler(const EventArgs&)
rc = send(GetFD(), (const char *)m_SendQueue->GetReadBuffer(), m_SendQueue->GetSize(), 0); rc = send(GetFD(), (const char *)m_SendQueue->GetReadBuffer(), m_SendQueue->GetSize(), 0);
if (rc <= 0) { if (rc <= 0) {
HandleSocketError(); HandleSocketError(SocketException("send() failed", GetError()));
return 0; return 0;
} }

View File

@ -67,7 +67,8 @@ void TcpServer::Listen(void)
int rc = listen(GetFD(), SOMAXCONN); int rc = listen(GetFD(), SOMAXCONN);
if (rc < 0) { if (rc < 0) {
HandleSocketError(); HandleSocketError(SocketException(
"listen() failed", GetError()));
return; return;
} }
} }
@ -88,7 +89,8 @@ int TcpServer::ReadableEventHandler(const EventArgs&)
fd = accept(GetFD(), (sockaddr *)&addr, &addrlen); fd = accept(GetFD(), (sockaddr *)&addr, &addrlen);
if (fd < 0) { if (fd < 0) {
HandleSocketError(); HandleSocketError(SocketException(
"accept() failed", GetError()));
return 0; return 0;
} }

View File

@ -33,7 +33,8 @@ void TcpSocket::MakeSocket(int family)
int fd = socket(family, SOCK_STREAM, 0); int fd = socket(family, SOCK_STREAM, 0);
if (fd == INVALID_SOCKET) { if (fd == INVALID_SOCKET) {
HandleSocketError(); HandleSocketError(SocketException(
"socket() failed", GetLastSocketError()));
return; return;
} }
@ -70,8 +71,10 @@ void TcpSocket::Bind(string node, string service, int family)
hints.ai_protocol = IPPROTO_TCP; hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE; hints.ai_flags = AI_PASSIVE;
if (getaddrinfo(node.empty() ? NULL : node.c_str(), service.c_str(), &hints, &result) < 0) { if (getaddrinfo(node.empty() ? NULL : node.c_str(),
HandleSocketError(); service.c_str(), &hints, &result) < 0) {
HandleSocketError(SocketException(
"getaddrinfo() failed", GetLastSocketError()));
return; return;
} }
@ -110,8 +113,8 @@ void TcpSocket::Bind(string node, string service, int family)
break; break;
} }
if (fd == INVALID_SOCKET)
HandleSocketError();
freeaddrinfo(result); freeaddrinfo(result);
if (fd == INVALID_SOCKET)
HandleSocketError(InvalidArgumentException());
} }

View File

@ -132,7 +132,8 @@ int TlsClient::ReadableEventHandler(const EventArgs&)
return 0; return 0;
default: default:
HandleSSLError(); HandleSocketError(OpenSSLException(
"SSL_read failed", ERR_get_error()));
return 0; return 0;
} }
@ -174,7 +175,8 @@ int TlsClient::WritableEventHandler(const EventArgs&)
return 0; return 0;
default: default:
HandleSSLError(); HandleSocketError(OpenSSLException(
"SSL_write failed", ERR_get_error()));
return 0; return 0;
} }
@ -229,24 +231,6 @@ void TlsClient::CloseInternal(bool from_dtor)
TcpClient::CloseInternal(from_dtor); TcpClient::CloseInternal(from_dtor);
} }
/**
* Handles an OpenSSL error.
*/
void TlsClient::HandleSSLError(void)
{
int code = ERR_get_error();
if (code != 0) {
SocketErrorEventArgs sea;
sea.Code = code;
sea.Message = OpenSSLException::FormatErrorCode(sea.Code);
OnError(sea);
}
Close();
return;
}
/** /**
* Factory function for the TlsClient class. * Factory function for the TlsClient class.
* *

View File

@ -69,7 +69,7 @@ long Variant::GetInteger(void) const
* *
* @returns The variant's value as a bool. * @returns The variant's value as a bool.
*/ */
long Variant::GetBool(void) const bool Variant::GetBool(void) const
{ {
Convert(VariantInteger); Convert(VariantInteger);

View File

@ -69,7 +69,7 @@ public:
VariantType GetType(void) const; VariantType GetType(void) const;
long GetInteger(void) const; long GetInteger(void) const;
long GetBool(void) const; bool GetBool(void) const;
string GetString(void) const; string GetString(void) const;
Object::Ptr GetObject(void) const; Object::Ptr GetObject(void) const;

View File

@ -132,7 +132,7 @@ int JsonRpcEndpoint::ClientClosedHandler(const EventArgs&)
int JsonRpcEndpoint::ClientErrorHandler(const SocketErrorEventArgs& ea) int JsonRpcEndpoint::ClientErrorHandler(const SocketErrorEventArgs& ea)
{ {
cerr << "Error occured for JSON-RPC socket: Code=" << ea.Code << "; Message=" << ea.Message << endl; cerr << "Error occured for JSON-RPC socket: Code=" << ea.Error.GetCode() << "; Message=" << ea.Error.GetMessage() << endl;
return 0; return 0;
} }