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