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)
{
m_Code = 0;
m_Message = NULL;
}
@ -36,6 +37,7 @@ Exception::Exception(void)
*/
Exception::Exception(const char *message)
{
m_Code = 0;
m_Message = NULL;
SetMessage(message);
}
@ -48,6 +50,26 @@ Exception::~Exception(void) throw()
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.
*

View File

@ -35,15 +35,18 @@ public:
Exception(const char *message);
virtual ~Exception(void) throw();
int GetCode(void) const;
const char *GetMessage(void) const;
virtual const char *what(void) const throw();
protected:
void SetCode(int code);
void SetMessage(const char *message);
private:
char *m_Message;
int m_Code;
};
#define DEFINE_EXCEPTION_CLASS(klass) \
@ -101,6 +104,7 @@ public:
{
string msg = message + ": " + FormatErrorCode(errorCode);
SetMessage(msg.c_str());
SetCode(errorCode);
}
/**
@ -129,6 +133,7 @@ public:
{
string msg = message + ": " + FormatErrorCode(errorCode);
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:
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;
socklen_t optlen = sizeof(opt);
int rc = getsockopt(GetFD(), SOL_SOCKET, SO_ERROR, (char *)&opt, &optlen);
if (rc >= 0 && opt != 0) {
SocketErrorEventArgs sea;
sea.Code = opt;
#ifdef _WIN32
sea.Message = Win32Exception::FormatErrorCode(sea.Code);
#else /* _WIN32 */
sea.Message = PosixException::FormatErrorCode(sea.Code);
#endif /* _WIN32 */
OnError(sea);
}
if (rc >= 0)
return opt;
Close();
return;
return 0;
}
/**
* 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&)
{
HandleSocketError();
HandleSocketError(SocketException(
"select() returned fd in except fdset", GetError()));
return 0;
}
@ -218,7 +244,8 @@ string Socket::GetClientAddress(void)
socklen_t len = sizeof(sin);
if (getsockname(GetFD(), (sockaddr *)&sin, &len) < 0) {
HandleSocketError();
HandleSocketError(SocketException(
"getsockname() failed", GetError()));
return string();
}
@ -237,10 +264,29 @@ string Socket::GetPeerAddress(void)
socklen_t len = sizeof(sin);
if (getpeername(GetFD(), (sockaddr *)&sin, &len) < 0) {
HandleSocketError();
HandleSocketError(SocketException(
"getpeername() failed", GetError()));
return string();
}
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
{
int Code; /**< The error code. */
string Message; /**< A message describing the error. */
const Exception& Error;
SocketErrorEventArgs(const Exception& exception)
: Error(exception) { }
};
/**
@ -74,7 +76,10 @@ public:
protected:
Socket(void);
void HandleSocketError(void);
int GetError(void) const;
static int GetLastSocketError(void);
void HandleSocketError(const Exception& exception);
virtual void CloseInternal(bool from_dtor);
private:
@ -85,6 +90,15 @@ private:
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 */

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

View File

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

View File

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

View File

@ -132,7 +132,8 @@ int TlsClient::ReadableEventHandler(const EventArgs&)
return 0;
default:
HandleSSLError();
HandleSocketError(OpenSSLException(
"SSL_read failed", ERR_get_error()));
return 0;
}
@ -174,7 +175,8 @@ int TlsClient::WritableEventHandler(const EventArgs&)
return 0;
default:
HandleSSLError();
HandleSocketError(OpenSSLException(
"SSL_write failed", ERR_get_error()));
return 0;
}
@ -229,24 +231,6 @@ void TlsClient::CloseInternal(bool 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.
*

View File

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

View File

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

View File

@ -132,7 +132,7 @@ int JsonRpcEndpoint::ClientClosedHandler(const EventArgs&)
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;
}