errors.cpp

jwatte's picture

#include "sockimpl.h"
#include <stdarg.h>
 
using namespace etwork;
using namespace etwork::impl;
 
#if !defined( NDEBUG )
bool etwork::impl::gDebugging = true;
#else
bool etwork::impl::gDebugging = false;
#endif
 
bool etwork::impl::wsOpen = false;
 
Lock etwork::impl::gethostLock; //!< gethostbyname is not thread safe
 
IErrorNotify * etwork::impl::gErrorNotify;
 
void SetEtworkErrorNotify( IErrorNotify * notify )
{
  gErrorNotify = notify;
}
 
 
static EtworkError GetWsaError( int wsaError, ErrorArea area )
{
  ErrorOption eo = EO_unknown_error;
  ErrorSeverity es = ES_error;
  switch( wsaError ) {
  case WSAEINTR:
    break;
  case WSAEACCES:         eo = EO_already_in_use;
    break;
  case WSAEFAULT:         eo = EO_invalid_parameters;
    break;
  case WSAEINVAL:         eo = EO_invalid_parameters;
    break;
  case WSAEMFILE:         eo = EO_out_of_resources; es = ES_catastrophe;
    break;
  case WSAEWOULDBLOCK:    es = ES_warning;
    break;
  case WSAEINPROGRESS:    es = ES_warning;
    break;
  case WSAEALREADY:       eo = EO_invalid_parameters;
    break;
  case WSAENOTSOCK:       eo = EO_invalid_parameters;
    break;
  case WSAEDESTADDRREQ:   eo = EO_invalid_parameters; es = ES_catastrophe;
    break;
  case WSAEMSGSIZE:       eo = EO_out_of_resources; es = ES_warning;
    break;
  case WSAEPROTOTYPE:     eo = EO_invalid_parameters;
    break;
  case WSAENOPROTOOPT:    eo = EO_invalid_parameters;
    break;
  case WSAEPROTONOSUPPORT:  eo = EO_unsupported_platform; es = ES_catastrophe;
    break;
  case WSAESOCKTNOSUPPORT:  eo = EO_unsupported_platform; es = ES_catastrophe;
    break;
  case WSAEOPNOTSUPP:     eo = EO_invalid_parameters;
    break;
  case WSAEPFNOSUPPORT:   eo = EO_unsupported_platform; es = ES_catastrophe;
    break;
  case WSAEAFNOSUPPORT:   eo = EO_unsupported_platform; es = ES_catastrophe;
    break;
  case WSAEADDRINUSE:     eo = EO_already_in_use;
    break;
  case WSAEADDRNOTAVAIL:  eo = EO_invalid_parameters;
    break;
  case WSAENETDOWN:       eo = EO_out_of_resources;
    break;
  case WSAENETUNREACH:    eo = EO_bad_address;
    break;
  case WSAENETRESET:      eo = EO_out_of_resources;
    break;
  case WSAECONNABORTED:   eo = EO_peer_timeout;
    break;
  case WSAECONNRESET:     eo = EO_peer_dropped;
    break;
  case WSAENOBUFS:        eo = EO_out_of_resources;
    break;
  case WSAEISCONN:        eo = EO_invalid_parameters; es = ES_catastrophe;
    break;
  case WSAENOTCONN:       eo = EO_invalid_parameters; es = ES_catastrophe;
    break;
  case WSAESHUTDOWN:      eo = EO_invalid_parameters;
    break;
  case WSAETIMEDOUT:      eo = EO_peer_timeout;
    break;
  case WSAECONNREFUSED:   eo = EO_peer_refused;
    break;
  case WSAEHOSTDOWN:      eo = EO_peer_timeout;
    break;
  case WSAEHOSTUNREACH:   eo = EO_bad_address;
    break;
  case WSAEPROCLIM:       eo = EO_out_of_resources; es = ES_catastrophe;
    break;
  case WSASYSNOTREADY:    eo = EO_unsupported_platform; es = ES_catastrophe;
    break;
  case WSAVERNOTSUPPORTED:  eo = EO_unsupported_version; es = ES_catastrophe;
    break;
  case WSANOTINITIALISED: es = ES_catastrophe;
    break;
  case WSAEDISCON:        eo = EO_peer_dropped;
    break;
  case WSATYPE_NOT_FOUND:
    break;
  case WSAHOST_NOT_FOUND: eo = EO_bad_address;
    break;
  case WSATRY_AGAIN:      eo = EO_bad_address;
    break;
  case WSANO_RECOVERY:    eo = EO_bad_address;
    break;
  case WSANO_DATA:        eo = EO_bad_address;
    break;
  case WSA_INVALID_HANDLE:  eo = EO_invalid_parameters; es = ES_catastrophe;
    break;
  case WSA_INVALID_PARAMETER: eo = EO_invalid_parameters; es = ES_catastrophe;
    break;
  case WSA_IO_INCOMPLETE:
    break;
  case WSA_IO_PENDING:
    break;
  case WSA_NOT_ENOUGH_MEMORY: eo = EO_out_of_resources;
    break;
  case WSA_OPERATION_ABORTED: eo = EO_peer_dropped;
    break;
  case WSASYSCALLFAILURE:
    break;
  default:
    {
      char buf[1024];
      _snprintf( buf, 1024, "Unknown system error code in GetWsaError(): %d\n", wsaError );
      buf[1023] = 0;
      OutputDebugString( buf );
    }
    break;
  }
 
  return EtworkError( es, area, eo );
}
 
 
std::string etwork::impl::get_error_string( int err )
{
  std::string error_;
  if( err == 0 ) {
    error_ = "";
  }
  else {
    char buf[2048];
    int l = ::FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0, buf, 2048, 0 );
    if( l < 0 || l > 2047 ) {
      l = 2047;
    }
    buf[l] = 0;
    error_ = buf;
    size_t pos;
    while( (pos = error_.find( '\n' )) != std::string::npos ) {
      error_.erase( pos, 1 );
    }
    while( (pos = error_.find( '\r' )) != std::string::npos ) {
      error_.erase( pos, 1 );
    }
    while( (pos = error_.find( '.' )) != std::string::npos ) {
      error_.erase( pos, 1 );
    }
  }
  return error_;
}
 
 
void etwork::impl::assert_failure( char const * expr, char const * file, int line )
{
  char str[2048];
  _snprintf( str, 2048, "Etwork: %s(%d): Assertion Failed: %s\n", 
      file, line, expr );
  str[2047] = 0;
  if( gErrorNotify ) {
    ErrorInfo info;
    info.error = EtworkError( ES_internal, EA_unknown, EO_unknown_error );
    info.osError = -1;
    info.socket = 0;
    info.error.setText( str );
    gErrorNotify->onSocketError( info );
  }
  else {
    OutputDebugString( str );
    if( gDebugging ) {
      if( ::MessageBox( NULL, str, "Etwork: Assert Failure", MB_OKCANCEL | MB_ICONSTOP ) 
          == IDCANCEL ) {
        OutputDebugString( "User pressed CANCEL on assert dialog; hitting breakpoint.\n" );
        __asm { int 3 }
      }
    }
  }
}
 
 
EtworkError::EtworkError()
{
  error_ = 0;
  text_ = 0;
}
 
EtworkError::EtworkError( int severity, int area, int option )
{
  ASSERT( (severity&ES_mask) == severity );
  ASSERT( (area&EA_mask) == area );
  ASSERT( (option&EO_mask) == option );
  error_ = severity | area | option;
  text_ = 0;
}
 
EtworkError::EtworkError( EtworkError const & o )
{
  error_ = o.error_;
  if( o.text_ ) {
    text_ = ::strdup( o.text_ );
  }
  else {
    text_ = 0;
  }
}
 
EtworkError::~EtworkError()
{
  ::free( text_ );
}
 
EtworkError & EtworkError::operator=( EtworkError const & o )
{
  if( text_ ) {
    ::free( text_ );
  }
  new( (void *)this ) EtworkError( o );
  return *this;
}
 
EtworkError & EtworkError::operator=( int err )
{
  error_ = err;
  return *this;
}
 
EtworkError::operator int() const
{
  return error_;
}
 
int EtworkError::severity() const
{
  return error_ & ES_mask;
}
 
int EtworkError::area() const
{
  return error_ & EA_mask;
}
 
int EtworkError::option() const
{
  return error_ & EO_mask;
}
 
bool EtworkError::operator==( EtworkError const & o ) const
{
  return error_ == o.error_;
}
 
bool EtworkError::operator!() const
{
  return !error_;
}
 
EtworkError::operator bool() const
{
  return !!error_;
}
 
static char const * severity_str( int sev )
{
  switch( sev & ES_mask ) {
    case ES_internal: return "internal error";
    case ES_catastrophe: return "catastrophic error"; 
    case ES_error: return "runtime error"; 
    case ES_warning: return "runtime warning"; 
    case ES_note: return "runtime note"; 
    default: return "illegal severity code"; 
  }
}
 
static char const * area_str( int area )
{
  switch( area & EA_mask ) {
    case EA_unknown: return "unknown area";
    case EA_dispose: return "dispose";
    case EA_session: return "session";
    case EA_buffer: return "buffer";
    case EA_connect: return "connection";
    case EA_address: return "address";
    case EA_init: return "initialization";
    default: return "illegal area code";
  }
}
 
static char const * option_str( int option )
{
  switch( option & EO_mask ) {
    case EO_no_error: return "no error";
    case EO_unknown_error: return "unknown error";
    case EO_unsupported_version: return "unsupported version";
    case EO_unsupported_platform: return "unsupported platform";
    case EO_invalid_parameters: return "invalid parameters";
    case EO_buffer_full: return "buffer full";
    case EO_out_of_resources: return "out of resources";
    case EO_bad_address: return "bad address";
    case EO_already_in_use: return "already in use";
    case EO_peer_refused: return "peer refused connection";
    case EO_peer_dropped: return "peer dropped connection";
    case EO_peer_timeout: return "peer timed out";
    case EO_peer_violation: return "peer violated protocol";
    default: return "illegal option code";
  }
}
 
 
 
 
char const * EtworkError::c_str() const
{
  if( !text_ ) {
    char buf[2048];
    _snprintf( buf, 2048, "%s in %s: %s",
        severity_str( severity() ), area_str( area() ), option_str( option() ) );
    buf[2047] = 0;
    text_ = ::strdup( buf );
  }
  return text_;
}
 
void EtworkError::setText( char const * text )
{
  if( text_ ) {
    ::free( text_ );
  }
  if( text ) {
    text_ = ::strdup( text );
  }
  else {
    text_ = 0;
  }
}
 
 
 
 
bool etwork::impl::wsa_error( int wsaErr, ErrorArea area )
{
  return wsa_error_from( (ISocket *)0, (ISocketManager *)0, wsaErr, area );
}
 
bool etwork::impl::wsa_error_from( ISocket * sock, int wsaErr, ErrorArea area )
{
  return wsa_error_from( sock, sock ? static_cast< Socket * >( sock )->mgr_ : 0, wsaErr, area );
}
 
bool etwork::impl::wsa_error_from( ISocket * sock, ISocketManager * mgr, int wsaErr, ErrorArea area )
{
  IErrorNotify * en = gErrorNotify;
  if( mgr ) {
    SocketManager * smgr = static_cast< SocketManager *> ( mgr );
    if( smgr->settings_.notify ) {
      en = smgr->settings_.notify;
    }
  }
  SocketManager * sm = static_cast< SocketManager * >( mgr );
  ErrorInfo info;
  info.error = GetWsaError( wsaErr, area );
  info.osError = wsaErr;
  info.socket = sock;
  if( en ) {
    en->onSocketError( info );
  }
  else if( info.error.severity() >= ES_error || (sm && sm->settings_.debug) || gDebugging ) {
    char buf[2048];
    _snprintf( buf, 2048, "Etwork: Error %d in wsa_error_from(): %s (%s)\n", wsaErr,
        info.error.c_str(), get_error_string( wsaErr ).c_str() );
    buf[2047] = 0;
    OutputDebugString( buf );
  }
  return info.error.severity() < ES_catastrophe;
}
 
bool etwork::impl::etwork_error_from( ISocket * sock, ISocketManager * mgr, EtworkError err )
{
  IErrorNotify * en = gErrorNotify;
  if( mgr ) {
    SocketManager * smgr = static_cast< SocketManager *> ( mgr );
    if( smgr->settings_.notify ) {
      en = smgr->settings_.notify;
    }
  }
  SocketManager * sm = static_cast< SocketManager * >( mgr );
  ErrorInfo info;
  info.error = err;
  info.osError = 0;
  info.socket = sock;
  if( en ) {
    en->onSocketError( info );
  }
  else if( (sm && sm->settings_.debug) || gDebugging ) {
    char buf[2048];
    _snprintf( buf, 2048, "Etwork: Error in etwork_error_from(): %s\n", info.error.c_str() );
    buf[2047] = 0;
    OutputDebugString( buf );
  }
  return info.error.severity() < ES_catastrophe;
}
 
bool etwork::impl::etwork_info_from( ISocketManager * mgr, ErrorInfo err )
{
  IErrorNotify * en = gErrorNotify;
  if( mgr ) {
    SocketManager * smgr = static_cast< SocketManager *> ( mgr );
    if( smgr->settings_.notify ) {
      en = smgr->settings_.notify;
    }
  }
  SocketManager * sm = static_cast< SocketManager * >( mgr );
  if( err.socket ) {
    sm = static_cast< SocketManager * >( static_cast< Socket * >( err.socket )->mgr_ );
    if( sm->settings_.notify ) {
      en = sm->settings_.notify;
    }
  }
  if( en ) {
    en->onSocketError( err );
  }
  else if( (sm && sm->settings_.debug) || gDebugging ) {
    char buf[2048];
    _snprintf( buf, 2048, "Etwork: Error in etwork_error_from(): %s (%s)\n", err.error.c_str(),
        get_error_string( err.osError ).c_str() );
    buf[2047] = 0;
    OutputDebugString( buf );
  }
  return err.error.severity() < ES_catastrophe;
}
 
void etwork::impl::etwork_log( ISocket * sock, ErrorSeverity sev, char const * text, ... )
{
  char buf[2048];
  va_list vl;
  va_start( vl, text );
  _vsnprintf( buf, 2048, text, vl );
  va_end( vl );
  buf[2047] = 0;
  ErrorInfo ei;
  ei.error = EtworkError( sev, EA_unknown, EO_no_error );
  ei.error.setText( buf );
  ei.osError = 0;
  ei.socket = 0;
  etwork_info_from( 0, ei );
}