/*! \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(); }