paradigm_3/html/
/*! \file connection.cpp
  This is the generic Connection class implementation.

  \author Jon A. Lambert
  \date 05/02/2003
  \version 0.30
 */
#include "sysconfig.h"
#include "connection.h"
#include "socket.h"
#include "server.h"
#include "log.h"

/*!
  Constructor for Connection.  There is no default constructor as
  all connections must be constructed with a SOCKET.
 */
Connection::Connection(SOCKET sock, Server* server)
  : mServer(server), mDisconnectable(false) {
  mSock = new Socket(sock);
}

/*!
  Destructor for Connection
 */
Connection::~Connection() {
  delete mSock;
}

/*!
  Copy Constructor for Connection
 */
Connection::Connection(const Connection& r_conn)
  : mServer(r_conn.mServer), mDisconnectable(r_conn.mDisconnectable),
    mInBuffer(r_conn.mInBuffer), mOutBuffer(r_conn.mOutBuffer),
    mPendingInput(r_conn.mPendingInput) {
  mSock = new Socket(*r_conn.mSock);
}

/*!
  Assignment operator for Connection
 */
Connection & Connection::operator=(const Connection& r_conn) {
  if (this == &r_conn)
    return *this;
  mServer = r_conn.mServer;
  mInBuffer = r_conn.mInBuffer;
  mOutBuffer = r_conn.mOutBuffer;
  mPendingInput = r_conn.mPendingInput;
  delete mSock;
  mSock = new Socket(*r_conn.mSock);
  return *this;
}

/*!
  Equality operator for Connection.  If the SOCKET is the same they are
  equal.
 */
bool Connection::operator==(const Connection& r_conn) const {
  return (GetSocket() == r_conn.GetSocket());
}

/*!
 HandleInput is called to order a connection to process any input
 waiting on its socket.  Input is parsed into lines based on the
 occurance of the CRLF terminator, which is stripped and pushed into
 a buffer which is a list of lines.  The buffer expands dynamically as
 input is processed.  Input that has yet to see a CRLF terminator
 is left in the Connection's pPendingInput array.

 \see Server::Run
 */
void Connection::HandleInput() {
  // Loop to gather all socket input.
  while(true) {
    char c;
    try {
      c = mSock->Read();
    }
    catch (BlockingException &ex) {
      // Indicates no more input can be squeezed from this socket.
      // Let's bust this loop
      mServer->ServerLog().Write("INFO-Connection(recv): Blocking %d on %d",
        ex.mErrCode, GetSocket());
      break;
    }
    catch (ShutdownException &ex) {
      mDisconnectable = true;
      // Connection is scheduled for disconnection, and we'll exit
      mServer->ServerLog().Write("WARNING-Connection(recv): Shutdown %d on %d",
        ex.mErrCode, GetSocket());
      return;
    }
    catch (IOErrorException &ex) {
      // We have a serious error
      // Connection is scheduled for disconnection, and we'll exit
      mServer->ServerLog().Write("ERROR-Connection(recv): IOError %d on %d",
        ex.mErrCode, GetSocket());
      mDisconnectable = true;
      return;
    }
    // Append everything read to our string.
    mPendingInput += c;
    // Let's yield every 4K of input
    if (mPendingInput.size() > 4096)
      break;
  } // while

  // Loop to parse all the input into lines for our buffer
  while(true) {
    int pos = mPendingInput.find("\r\n");
    if (pos != (int)mPendingInput.npos) {
      // A line was found; save it to buffer and remove from input string
      mInBuffer.push_back(mPendingInput.substr(0,pos));
      mPendingInput.erase(0,pos + 2);
    } else {
      // No	more complete lines found
      break;
    }
  }
}

/*!
 HandleOutput is called to order a connection to process any output
 waiting on its socket.

 \notes
 Unix/BSD portability notes:
   - Either send() or write() may be used.
   - Winsock errors are prepended with WSA modify accordingly.
   - WSAGetLastError() should be replaced with global errno variable.

 \see Server::Run
 */
void Connection::HandleOutput() {
  // If there is any output waiting in line buffer write it out
  while (!mOutBuffer.empty()) {
    try {
      mSock->Write(mOutBuffer.front());
      mOutBuffer.pop_front();
      mSock->Flush();
    }
    catch (BlockingException &ex) {
      // :bug: we don't save where we last were in the output.
      mServer->ServerLog().Write("INFO-Connection(send): Blocking %d on %d",
        ex.mErrCode, GetSocket());
      return;
    }
    catch (IOErrorException &ex) {
      // severe error set socket as disconnectable and leave
      mServer->ServerLog().Write("ERROR-Connection(send): IOError %d on %d",
        ex.mErrCode, GetSocket());
      mDisconnectable = true;
      return;
    }
  } // while
}

/*!
 Disconnect is called to shutdown a Connection's socket.
 */
void Connection::Disconnect() {
  mServer->ServerLog().Write(
    "INFO-Connection(disconnect): Connection %d disconnected", GetSocket());
  mSock->Close();
}

/*!
 SendMsg places a message on the Connection's output buffer.

 \param r_msg the message, a reference to a string
 */
void Connection::SendMsg(const string& r_msg) {
  mOutBuffer.push_back(r_msg);
}

/*!
 ReadMsg removes a message from the Connection's input buffer.

 \return the message, a new string or NULL if no messages

 \warning The caller is responsible for deleting this string!
 This can return an empty string, which indicates a CRLF only was sent.
 */
string* Connection::ReadMsg() {
  if (!mInBuffer.empty()) {
	  string * msg = new string(mInBuffer.front());
	  mInBuffer.pop_front();
	  return msg;
  }
  return NULL;
}

/*!
 CanBeDisconnected is called to query the Connection in order to check
 whether it has been scheduled for disconnection.  Currently this status
 is set only when there is an error.

 \return a boolean value indicating whether the connection is to be
         disconnected.
 */
bool Connection::CanBeDisconnected() {
  return mDisconnectable;
}

/*!
 GetSocket

 \return the Connection's SOCKET value.
 */
SOCKET Connection::GetSocket() const {
  return mSock->GetSocket();
}

/*!
 HasOutput is called to query whether the Connection has output
 ready to send.

 \return a boolean value indicating whether output is pending.
 */
bool Connection::HasOutput() {
  return !mOutBuffer.empty();
}