You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
arts/mcop/connection.cc

197 lines
4.2 KiB

/*
Copyright (C) 2000 Stefan Westerfeld
stefan@space.twc.de
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "connection.h"
#include "dispatcher.h"
#include "debug.h"
#include <stdio.h>
#include <string.h> // for Solaris
#include <queue>
#include <algorithm>
using namespace Arts;
using namespace std;
namespace Arts {
class ConnectionPrivate {
public:
struct Data {
Data() : data(0), len(0) { }
Data(unsigned char *data, long len) : data(data), len(len) { }
Data(const Data& d) : data(d.data), len(d.len) { }
unsigned char *data;
long len;
};
queue<Data> incoming;
map<string,string> hints;
};
}
Connection::Connection() :d(new ConnectionPrivate), _refCnt(1)
{
_connState = unknown;
}
Connection::~Connection()
{
assert(d->incoming.empty());
assert(_refCnt == 0);
delete d;
}
void Connection::_copy()
{
assert(_refCnt > 0);
_refCnt++;
}
void Connection::_release()
{
assert(_refCnt > 0);
_refCnt--;
if(_refCnt == 0)
delete this;
}
void Connection::initReceive()
{
rcbuf = 0;
receiveHeader = true;
remaining = 12;
}
void Connection::receive(unsigned char *newdata, long newlen)
{
/*
* protect against being freed while receive is running, as there are a
* few points where reentrant event loops may happen (Dispatcher::handle)
*/
_copy();
d->incoming.push(ConnectionPrivate::Data(newdata,newlen));
do
{
ConnectionPrivate::Data &data = d->incoming.front();
// get a buffer for the incoming message
if(!rcbuf) rcbuf = new Buffer;
// put a suitable amount of input data into the receive buffer
long len = min(remaining, data.len);
remaining -= len;
rcbuf->write(data.data,len);
data.len -= len;
data.data += len;
if(data.len == 0)
d->incoming.pop();
// look if it was enough to do something useful with it
if(remaining == 0)
{
if(receiveHeader)
{
long mcopMagic;
mcopMagic = rcbuf->readLong();
remaining = rcbuf->readLong() - 12;
messageType = rcbuf->readLong();
if(_connState != Connection::established
&& (remaining >= 4096 || remaining < 0))
{
/*
* don't accept large amounts of data on unauthenticated
* connections
*/
remaining = 0;
}
if(mcopMagic == MCOP_MAGIC)
{
// do we need to receive more data (message body?)
if(remaining)
{
receiveHeader = false;
}
else
{
Buffer *received = rcbuf;
initReceive();
Dispatcher::the()->handle(this,received,messageType);
}
}
else
{
initReceive();
Dispatcher::the()->handleCorrupt(this);
}
}
else
{
Buffer *received = rcbuf;
/*
* it's important to prepare to receive new messages *before*
* calling Dispatcher::the()->handle(...), as handle may
* get into an I/O situation (e.g. when doing an invocation
* itself), and we may receive more messages while handle is
* running
*/
initReceive();
// rcbuf is consumed by the dispatcher
Dispatcher::the()->handle(this,received,messageType);
}
}
} while(!d->incoming.empty());
_release();
}
void Connection::setHints(const vector<string>& hints)
{
vector<string>::const_iterator i;
for(i = hints.begin(); i != hints.end(); i++)
{
string key;
vector<string> values;
if(MCOPUtils::tokenize(*i, key, values))
{
if(values.size() == 1)
d->hints[key] = values[0];
}
}
}
string Connection::findHint(const string& hint)
{
return d->hints[hint];
}