/*! \file connection.cpp This is the generic Connection class implementation. \author Jon A. Lambert \date 02/15/2003 \version 0.10 */ #include "sysconfig.h" #include "connection.h" /*! Constructor for Connection. There is no default constructor as all connections must be constructed with a socket. */ //##ModelId=3ED44BF10253 Connection::Connection(SOCKET sock) : mSock(sock), mpPendingInput(NULL), mpPendingOutput(NULL), mDisconnectable(false) {} /*! Destructor for Connection */ //##ModelId=3ED44BF10255 Connection::~Connection() { delete[] mpPendingOutput; delete[] mpPendingInput; } /*! Copy Constructor for Connection */ //##ModelId=3ED44BF1025E Connection::Connection(const Connection& r_conn) : mDisconnectable(r_conn.mDisconnectable), mSock(r_conn.mSock), mpPendingInput(NULL), mpPendingOutput(NULL) { mInBuffer = r_conn.mInBuffer; mOutBuffer = r_conn.mOutBuffer; if (r_conn.mpPendingInput) { mpPendingInput = new char[strlen(r_conn.mpPendingInput) + 1]; strcpy(mpPendingInput, r_conn.mpPendingInput); } if (r_conn.mpPendingOutput) { mpPendingOutput = new char[strlen(r_conn.mpPendingOutput) + 1]; strcpy(mpPendingOutput, r_conn.mpPendingOutput); } } /*! Assignment operator for Connection */ //##ModelId=3ED44BF10288 Connection & Connection::operator=(const Connection& r_conn) { if (this == &r_conn) return * this; mInBuffer = r_conn.mInBuffer; mOutBuffer = r_conn.mOutBuffer; if (r_conn.mpPendingInput) { mpPendingInput = new char[strlen(r_conn.mpPendingInput) + 1]; strcpy(mpPendingInput, r_conn.mpPendingInput); } if (r_conn.mpPendingOutput) { mpPendingOutput = new char[strlen(r_conn.mpPendingOutput) + 1]; strcpy(mpPendingOutput, r_conn.mpPendingOutput); } return *this; } /*! 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 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. \notes Unix/BSD portability notes: - Either read() or recv() may be used. - Winsock errors are prepended with WSA modify accordingly. - WSAGetLastError() should be replaced with global errno variable. \see Server::Run */ //##ModelId=3ED44BF10260 void Connection::HandleInput() { string strin; char inbuf[4096]; // If there was input leftover from the last call to HandleInput // prepend it to the input string and clear it. if (mpPendingInput) { strin.append(mpPendingInput); delete[] mpPendingInput; mpPendingInput = NULL; } // Loop to gather all socket input in 4K chunks. while (true) { int nbytes = recv (mSock, inbuf, 4096, 0); if (nbytes == 0) { // Connection is scheduled for disconnection, and we'll exit mDisconnectable = true; return ; } else if (nbytes == SOCKET_ERROR) { int err = WSAGetLastError (); if (err == WSAEWOULDBLOCK) { // Indicates no more input can be squeezed from this socket. // Let's bust this loop break; } else { // We have a serious error // Connection is scheduled for disconnection, and we'll exit cout << "Error-Connection(recv):" << err << " on " << mSock << endl; mDisconnectable = true; return ; } } // Append everything read to our string. inbuf[nbytes] = '\0'; strin.append(inbuf); } // Loop to parse all input into lines for our buffer while (true) { int pos = strin.find("\r\n"); if (pos != (int)strin.npos) { // A line was found; save it to buffer and remove from input string mInBuffer.push_back(strin.substr(0, pos)); strin.erase(0, pos + 2); } else { // No more complete lines found if (!strin.empty()) { // Save leftovers into pending input array mpPendingInput = new char[strin.size() + 1]; strcpy(mpPendingInput, strin.c_str()); } 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 */ //##ModelId=3ED44BF10268 void Connection::HandleOutput() { string strout; // If there is output left to be sent from last time append // it to our output string if (mpPendingOutput) { strout.append(mpPendingOutput); delete[] mpPendingOutput; mpPendingOutput = NULL; } // If there is any output waiting in line buffer append the first line if (!mOutBuffer.empty()) { strout.append(mOutBuffer.front()); mOutBuffer.pop_front(); } // Loop until our output string is empty while (!strout.empty()) { const char * pstr = strout.c_str(); while (pstr) { // Loop until we have written this string int nbytes = send (mSock, pstr, strlen(pstr), 0); if (nbytes == SOCKET_ERROR) { int err = WSAGetLastError (); if (err == WSAEWOULDBLOCK) { // socket will block - save unwritten output in pending array // and leave mpPendingOutput = new char[strlen(pstr) + 1]; strcpy(mpPendingOutput, pstr); return ; } // severe error set socket as disconnectable and leave cout << "Error-Connection(recv):" << err << " on " << mSock << endl; mDisconnectable = true; return ; } else if (nbytes < (int)strlen(pstr)) { pstr += nbytes; } else { pstr = NULL; } } strout.erase(); // fetch next line in buffer if not empty if (!mOutBuffer.empty()) { strout.append(mOutBuffer.front()); mOutBuffer.pop_front(); } } } /*! Disconnect is called to shutdown a Connection's socket. */ //##ModelId=3ED44BF10272 void Connection::Disconnect() { char inbuf[4096]; bool done = false; shutdown(mSock, SD_SEND); while (!done) { int nbytes = recv (mSock, inbuf, 4096, 0); if (!nbytes || nbytes == SOCKET_ERROR) done = true; } closesocket(mSock); } /*! SendMsg places a message on the Connection's output buffer. \param r_msg the message, a reference to a string */ //##ModelId=3ED44BF10274 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 */ //##ModelId=3ED44BF1027D string* Connection::ReadMsg() { if (!mInBuffer.empty()) { string msg = mInBuffer.front(); mInBuffer.pop_front(); return new string(msg); } return NULL; } /*! CanBeDisconnected is called to query the Connection in order to see whether it wants to be disconnected. Currently this status is set only when there is an error. \return a boolean value indicating whether the connection is to be disconnected. */ //##ModelId=3ED44BF1026A bool Connection::CanBeDisconnected() { return mDisconnectable; } /*! GetSocket \return the Connection's SOCKET value. */ //##ModelId=3ED44BF10286 SOCKET Connection::GetSocket() { return mSock; }