adchpp-docker/src/adchpp/Client.cpp

175 lines
4.3 KiB
C++

/*
* Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "adchpp.h"
#include "Client.h"
#include "ClientManager.h"
#include "TimeUtil.h"
#include "SocketManager.h"
#include "Core.h"
namespace adchpp {
using namespace std;
using namespace std::placeholders;
Client* Client::create(ClientManager &cm, const ManagedSocketPtr& ms, uint32_t sid) throw() {
Client* c = new Client(cm, sid);
c->setSocket(ms);
return c;
}
Client::Client(ClientManager &cm, uint32_t sid_) throw() :
Entity(cm, sid_),
disconnecting(false),
dataBytes(0)
{
}
Client::~Client() {
}
namespace {
// Lightweight call forwarders, instead of std::bind
template<void (Client::*F)()>
struct Handler0 {
Handler0(Client* c_) : c(c_) { }
void operator()() { (c->*F)(); }
Client* c;
};
template<typename T, void (Client::*F)(const T&)>
struct Handler1 {
Handler1(Client* c_) : c(c_) { }
void operator()(const T& bv) { (c->*F)(bv); }
Client* c;
};
template<typename T, typename T2, void (Client::*F)(T, const T2&)>
struct Handler2 {
Handler2(Client* c_) : c(c_) { }
void operator()(const T& t, const T2& t2) { (c->*F)(t, t2); }
Client* c;
};
}
void Client::setSocket(const ManagedSocketPtr& aSocket) throw() {
dcassert(!socket);
socket = aSocket;
socket->setConnectedHandler(Handler0<&Client::onConnected>(this));
socket->setReadyHandler(Handler0<&Client::onReady>(this));
socket->setDataHandler(Handler1<BufferPtr, &Client::onData>(this));
socket->setFailedHandler(Handler2<Util::Reason, std::string, &Client::onFailed>(this));
}
void Client::onConnected() throw() {
cm.onConnected(*this);
}
void Client::onReady() throw() {
cm.onReady(*this);
}
void Client::onData(const BufferPtr& buf) throw() {
uint8_t* data = buf->data();
size_t done = 0;
size_t len = buf->size();
while(!disconnecting && done < len) {
if(dataBytes > 0) {
size_t n = (size_t)min(dataBytes, (int64_t)(len - done));
dataHandler(*this, data + done, n);
dataBytes -= n;
done += n;
} else {
size_t j = done;
while(j < len && data[j] != '\n')
++j;
if(j == len) {
if(!buffer) {
if(done == 0) {
buffer = buf;
} else {
buffer = make_shared<Buffer>(data + done, len - done);
}
} else {
buffer->append(data + done, data + len);
}
return;
} else if(!buffer) {
if(done == 0 && j == len-1) {
buffer = buf;
} else {
buffer = make_shared<Buffer>(data + done, j - done + 1);
}
} else {
buffer->append(data + done, data + j + 1);
}
done = j + 1;
if(cm.getMaxCommandSize() > 0 && buffer->size() > cm.getMaxCommandSize()) {
send(AdcCommand(AdcCommand::SEV_FATAL, AdcCommand::ERROR_PROTOCOL_GENERIC, "Command too long"));
disconnect(Util::REASON_MAX_COMMAND_SIZE);
return;
}
if(buffer->size() == 1) {
buffer.reset();
continue;
}
try {
AdcCommand cmd(buffer);
if(cmd.getType() == 'H') {
cmd.setFrom(getSID());
} else if(cmd.getFrom() != getSID()) {
disconnect(Util::REASON_INVALID_SID);
return;
}
cm.onReceive(*this, cmd);
} catch(const ParseException&) {
cm.onBadLine(*this, string((char*)buffer->data(), buffer->size()));
}
buffer.reset();
}
}
}
void Client::disconnect(Util::Reason reason, const std::string &info) throw() {
dcassert(socket);
if(!disconnecting) {
dcdebug("%s disconnecting because %d\n", AdcCommand::fromSID(getSID()).c_str(), reason);
disconnecting = true;
socket->disconnect(reason, info);
}
}
void Client::onFailed(Util::Reason reason, const std::string &info) throw() {
cm.onFailed(*this, reason, info);
delete this;
}
}