Code owners
Assign users and groups as approvers for specific file changes. Learn more.
lltransfermanager.cpp 36.66 KiB
/**
* @file lltransfermanager.cpp
* @brief Improved transfer mechanism for moving data through the
* message system.
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "lltransfermanager.h"
#include "llerror.h"
#include "message.h"
#include "lldatapacker.h"
#include "lltransfersourcefile.h"
#include "lltransfersourceasset.h"
#include "lltransfertargetfile.h"
#include "lltransfertargetvfile.h"
const S32 MAX_PACKET_DATA_SIZE = 2048;
const S32 MAX_PARAMS_SIZE = 1024;
LLTransferManager gTransferManager;
LLTransferSource::stype_scfunc_map LLTransferSource::sSourceCreateMap;
//
// LLTransferManager implementation
//
LLTransferManager::LLTransferManager() :
mValid(FALSE)
{
S32 i;
for (i = 0; i < LLTTT_NUM_TYPES; i++)
{
mTransferBitsIn[i] = 0;
mTransferBitsOut[i] = 0;
}
}
LLTransferManager::~LLTransferManager()
{
// LLTransferManager should have been cleaned up by message system shutdown process
llassert(!mValid);
if (mValid)
{
// Usually happens if OS tries to kill viewer
cleanup();
}
}
void LLTransferManager::init()
{
if (mValid)
{
LL_ERRS() << "Double initializing LLTransferManager!" << LL_ENDL;
}
mValid = TRUE;
// Register message system handlers
gMessageSystem->setHandlerFunc("TransferRequest", processTransferRequest, NULL);
gMessageSystem->setHandlerFunc("TransferInfo", processTransferInfo, NULL);
gMessageSystem->setHandlerFunc("TransferPacket", processTransferPacket, NULL);
gMessageSystem->setHandlerFunc("TransferAbort", processTransferAbort, NULL);
}
void LLTransferManager::cleanup()
{
mValid = FALSE;
host_tc_map::iterator iter;
for (iter = mTransferConnections.begin(); iter != mTransferConnections.end(); iter++)
{
delete iter->second;
}
mTransferConnections.clear();
}
void LLTransferManager::updateTransfers()
{
host_tc_map::iterator iter,cur;
iter = mTransferConnections.begin();
while (iter !=mTransferConnections.end())
{
cur = iter;
iter++;
cur->second->updateTransfers();
}
}
void LLTransferManager::cleanupConnection(const LLHost &host)
{
host_tc_map::iterator iter;
iter = mTransferConnections.find(host);
if (iter == mTransferConnections.end())
{
// This can happen legitimately if we've never done a transfer, and we're
// cleaning up a circuit.
//LL_WARNS() << "Cleaning up nonexistent transfer connection to " << host << LL_ENDL;
return;
}
LLTransferConnection *connp = iter->second;
delete connp;
mTransferConnections.erase(iter);
}
LLTransferConnection *LLTransferManager::getTransferConnection(const LLHost &host)
{
host_tc_map::iterator iter;
iter = mTransferConnections.find(host);
if (iter == mTransferConnections.end())
{
mTransferConnections[host] = new LLTransferConnection(host);
return mTransferConnections[host];
}
return iter->second;
}
LLTransferSourceChannel *LLTransferManager::getSourceChannel(const LLHost &host, const LLTransferChannelType type)
{
LLTransferConnection *tcp = getTransferConnection(host);
if (!tcp)
{
return NULL;
}
return tcp->getSourceChannel(type);
}
LLTransferTargetChannel *LLTransferManager::getTargetChannel(const LLHost &host, const LLTransferChannelType type)
{
LLTransferConnection *tcp = getTransferConnection(host);
if (!tcp)
{
return NULL;
}
return tcp->getTargetChannel(type);
}
// virtual
LLTransferSourceParams::~LLTransferSourceParams()
{ }
LLTransferSource *LLTransferManager::findTransferSource(const LLUUID &transfer_id)
{
// This linear traversal could screw us later if we do lots of
// searches for sources. However, this ONLY happens right now
// in asset transfer callbacks, so this should be relatively quick.
host_tc_map::iterator iter;
for (iter = mTransferConnections.begin(); iter != mTransferConnections.end(); iter++)
{
LLTransferConnection *tcp = iter->second;
LLTransferConnection::tsc_iter sc_iter;
for (sc_iter = tcp->mTransferSourceChannels.begin(); sc_iter != tcp->mTransferSourceChannels.end(); sc_iter++)
{
LLTransferSourceChannel *scp = *sc_iter;
LLTransferSource *sourcep = scp->findTransferSource(transfer_id);
if (sourcep)
{
return sourcep;
}
}
}
return NULL;
}
//
// Message handlers
//
//static
void LLTransferManager::processTransferRequest(LLMessageSystem *msgp, void **)
{
//LL_INFOS() << "LLTransferManager::processTransferRequest" << LL_ENDL;
LLUUID transfer_id;
LLTransferSourceType source_type;
LLTransferChannelType channel_type;
F32 priority;
msgp->getUUID("TransferInfo", "TransferID", transfer_id);
S32 temp_source_type;
msgp->getS32("TransferInfo", "SourceType", temp_source_type);
source_type = (LLTransferSourceType)temp_source_type;
S32 temp_channel_type;
msgp->getS32("TransferInfo", "ChannelType", temp_channel_type);
channel_type = (LLTransferChannelType)temp_channel_type;
msgp->getF32("TransferInfo", "Priority", priority);
LLTransferSourceChannel *tscp = gTransferManager.getSourceChannel(msgp->getSender(), channel_type);
if (!tscp)
{
LL_WARNS() << "Source channel not found" << LL_ENDL;
return;
}
if (tscp->findTransferSource(transfer_id))
{
LL_WARNS() << "Duplicate request for transfer " << transfer_id << ", aborting!" << LL_ENDL;
return;
}
S32 size = msgp->getSize("TransferInfo", "Params");
if(size > MAX_PARAMS_SIZE)
{
LL_WARNS() << "LLTransferManager::processTransferRequest params too big."
<< LL_ENDL;
return;
}
//LL_INFOS() << transfer_id << ":" << source_type << ":" << channel_type << ":" << priority << LL_ENDL;
LLTransferSource* tsp = LLTransferSource::createSource(
source_type,
transfer_id,
priority);
if(!tsp)
{
LL_WARNS() << "LLTransferManager::processTransferRequest couldn't create"
<< " transfer source!" << LL_ENDL;
return;
}
U8 tmp[MAX_PARAMS_SIZE];
msgp->getBinaryData("TransferInfo", "Params", tmp, size);
LLDataPackerBinaryBuffer dpb(tmp, MAX_PARAMS_SIZE);
BOOL unpack_ok = tsp->unpackParams(dpb);
if (!unpack_ok)
{
// This should only happen if the data is corrupt or
// incorrectly packed.
// *NOTE: We may want to call abortTransfer().
LL_WARNS() << "LLTransferManager::processTransferRequest: bad parameters."
<< LL_ENDL;
delete tsp;
return;
}
tscp->addTransferSource(tsp);
tsp->initTransfer();
}
//static
void LLTransferManager::processTransferInfo(LLMessageSystem *msgp, void **)
{
//LL_INFOS() << "LLTransferManager::processTransferInfo" << LL_ENDL;
LLUUID transfer_id;
LLTransferChannelType channel_type;
LLTSCode status;
S32 size;
msgp->getUUID("TransferInfo", "TransferID", transfer_id);
S32 temp_channel_type;
msgp->getS32("TransferInfo", "ChannelType", temp_channel_type);
channel_type = (LLTransferChannelType)temp_channel_type;
S32 temp_status;
msgp->getS32("TransferInfo", "Status", temp_status);
status = (LLTSCode)temp_status;
msgp->getS32("TransferInfo", "Size", size);
//LL_INFOS() << transfer_id << ":" << target_type<< ":" << channel_type << LL_ENDL;
LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
if (!ttcp)
{
LL_WARNS() << "Target channel not found" << LL_ENDL;
// Should send a message to abort the transfer.
return;
}
LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
if (!ttp)
{
LL_WARNS() << "TransferInfo for unknown transfer! Not able to handle this yet!" << LL_ENDL;
// This could happen if we're doing a push transfer, although to avoid confusion,
// maybe it should be a different message.
return;
}
if (status != LLTS_OK)
{
LL_WARNS() << transfer_id << ": Non-ok status, cleaning up" << LL_ENDL;
ttp->completionCallback(status);
// Clean up the transfer.
ttcp->deleteTransfer(ttp);
return;
}
// unpack the params
S32 params_size = msgp->getSize("TransferInfo", "Params");
if(params_size > MAX_PARAMS_SIZE)
{
LL_WARNS() << "LLTransferManager::processTransferInfo params too big."
<< LL_ENDL;
return;
}
else if(params_size > 0)
{
U8 tmp[MAX_PARAMS_SIZE];
msgp->getBinaryData("TransferInfo", "Params", tmp, params_size);
LLDataPackerBinaryBuffer dpb(tmp, MAX_PARAMS_SIZE);
if (!ttp->unpackParams(dpb))
{
// This should only happen if the data is corrupt or
// incorrectly packed.
LL_WARNS() << "LLTransferManager::processTransferRequest: bad params."
<< LL_ENDL;
ttp->abortTransfer();
ttcp->deleteTransfer(ttp);
return;
}
}
//LL_INFOS() << "Receiving " << transfer_id << ", size " << size << " bytes" << LL_ENDL;
ttp->setSize(size);
ttp->setGotInfo(TRUE);
// OK, at this point we to handle any delayed transfer packets (which could happen
// if this packet was lost)
// This is a lame cut and paste of code down below. If we change the logic down there,
// we HAVE to change the logic up here.
while (1)
{
S32 packet_id = 0;
U8 tmp_data[MAX_PACKET_DATA_SIZE];
// See if we've got any delayed packets
packet_id = ttp->getNextPacketID();
if (ttp->mDelayedPacketMap.find(packet_id) != ttp->mDelayedPacketMap.end())
{
// Perhaps this stuff should be inside a method in LLTransferPacket?
// I'm too lazy to do it now, though.
// LL_INFOS() << "Playing back delayed packet " << packet_id << LL_ENDL;
LLTransferPacket *packetp = ttp->mDelayedPacketMap[packet_id];
// This is somewhat inefficient, but avoids us having to duplicate
// code between the off-the-wire and delayed paths.
packet_id = packetp->mPacketID;
size = packetp->mSize;
if (size)
{
if ((packetp->mDatap != NULL) && (size<(S32)sizeof(tmp_data)))
{
memcpy(tmp_data, packetp->mDatap, size); /*Flawfinder: ignore*/
}
}
status = packetp->mStatus;
ttp->mDelayedPacketMap.erase(packet_id);
delete packetp;
}
else
{
// No matching delayed packet, we're done.
break;
}
LLTSCode ret_code = ttp->dataCallback(packet_id, tmp_data, size);
if (ret_code == LLTS_OK)
{
ttp->setLastPacketID(packet_id);
}
if (status != LLTS_OK)
{
if (status != LLTS_DONE)
{
LL_WARNS() << "LLTransferManager::processTransferInfo Error in playback!" << LL_ENDL;
}
else
{
LL_INFOS() << "LLTransferManager::processTransferInfo replay FINISHED for " << transfer_id << LL_ENDL;
}
// This transfer is done, either via error or not.
ttp->completionCallback(status);
ttcp->deleteTransfer(ttp);
return;
}
}
}
//static
void LLTransferManager::processTransferPacket(LLMessageSystem *msgp, void **)
{
//LL_INFOS() << "LLTransferManager::processTransferPacket" << LL_ENDL;
LLUUID transfer_id;
LLTransferChannelType channel_type;
S32 packet_id;
LLTSCode status;
S32 size;
msgp->getUUID("TransferData", "TransferID", transfer_id);
S32 temp_channel_type;
msgp->getS32("TransferData", "ChannelType", temp_channel_type);
channel_type = (LLTransferChannelType)temp_channel_type;
msgp->getS32("TransferData", "Packet", packet_id);
S32 temp_status;
msgp->getS32("TransferData", "Status", temp_status);
status = (LLTSCode)temp_status;
// Find the transfer associated with this packet.
//LL_INFOS() << transfer_id << ":" << channel_type << LL_ENDL;
LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
if (!ttcp)
{
LL_WARNS() << "Target channel not found" << LL_ENDL;
return;
}
LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
if (!ttp)
{
LL_WARNS() << "Didn't find matching transfer for " << transfer_id
<< " processing packet " << packet_id
<< " from " << msgp->getSender() << LL_ENDL;
return;
}
size = msgp->getSize("TransferData", "Data");
S32 msg_bytes = 0;
if (msgp->getReceiveCompressedSize())
{
msg_bytes = msgp->getReceiveCompressedSize();
}
else
{
msg_bytes = msgp->getReceiveSize();
}
gTransferManager.addTransferBitsIn(ttcp->mChannelType, msg_bytes*8);
if ((size < 0) || (size > MAX_PACKET_DATA_SIZE))
{
LL_WARNS() << "Invalid transfer packet size " << size << LL_ENDL;
return;
}
U8 tmp_data[MAX_PACKET_DATA_SIZE];
if (size > 0)
{
// Only pull the data out if the size is > 0
msgp->getBinaryData("TransferData", "Data", tmp_data, size);
}
if ((!ttp->gotInfo()) || (ttp->getNextPacketID() != packet_id))
{
// Put this on a list of packets to be delivered later.
if(!ttp->addDelayedPacket(packet_id, status, tmp_data, size))
{
// Whoops - failed to add a delayed packet for some reason.
LL_WARNS() << "Too many delayed packets processing transfer "
<< transfer_id << " from " << msgp->getSender() << LL_ENDL;
ttp->abortTransfer();
ttcp->deleteTransfer(ttp);
return;
}
#if 0
// Spammy!
const S32 LL_TRANSFER_WARN_GAP = 10;
if(!ttp->gotInfo())
{
LL_WARNS() << "Got data packet before information in transfer "
<< transfer_id << " from " << msgp->getSender()
<< ", got " << packet_id << LL_ENDL;
}
else if((packet_id - ttp->getNextPacketID()) > LL_TRANSFER_WARN_GAP)
{
LL_WARNS() << "Out of order packet in transfer " << transfer_id
<< " from " << msgp->getSender() << ", got " << packet_id
<< " expecting " << ttp->getNextPacketID() << LL_ENDL;
}
#endif
return;
}
// Loop through this until we're done with all delayed packets
//
// NOTE: THERE IS A CUT AND PASTE OF THIS CODE IN THE TRANSFERINFO HANDLER
// SO WE CAN PLAY BACK DELAYED PACKETS THERE!!!!!!!!!!!!!!!!!!!!!!!!!
//
BOOL done = FALSE;
while (!done)
{
LLTSCode ret_code = ttp->dataCallback(packet_id, tmp_data, size);
if (ret_code == LLTS_OK)
{
ttp->setLastPacketID(packet_id);
}
if (status != LLTS_OK)
{
if (status != LLTS_DONE)
{
LL_WARNS() << "LLTransferManager::processTransferPacket Error in transfer!" << LL_ENDL;
}
else
{
// LL_INFOS() << "LLTransferManager::processTransferPacket done for " << transfer_id << LL_ENDL;
}
// This transfer is done, either via error or not.
ttp->completionCallback(status);
ttcp->deleteTransfer(ttp);
return;
}
// See if we've got any delayed packets
packet_id = ttp->getNextPacketID();
if (ttp->mDelayedPacketMap.find(packet_id) != ttp->mDelayedPacketMap.end())
{
// Perhaps this stuff should be inside a method in LLTransferPacket?
// I'm too lazy to do it now, though.
// LL_INFOS() << "Playing back delayed packet " << packet_id << LL_ENDL;
LLTransferPacket *packetp = ttp->mDelayedPacketMap[packet_id];
// This is somewhat inefficient, but avoids us having to duplicate
// code between the off-the-wire and delayed paths.
packet_id = packetp->mPacketID;
size = packetp->mSize;
if (size)
{
if ((packetp->mDatap != NULL) && (size<(S32)sizeof(tmp_data)))
{
memcpy(tmp_data, packetp->mDatap, size); /*Flawfinder: ignore*/
}
}
status = packetp->mStatus;
ttp->mDelayedPacketMap.erase(packet_id);
delete packetp;
}
else
{
// No matching delayed packet, abort it.
done = TRUE;
}
}
}
//static
void LLTransferManager::processTransferAbort(LLMessageSystem *msgp, void **)
{
//LL_INFOS() << "LLTransferManager::processTransferPacket" << LL_ENDL;
LLUUID transfer_id;
LLTransferChannelType channel_type;
msgp->getUUID("TransferInfo", "TransferID", transfer_id);
S32 temp_channel_type;
msgp->getS32("TransferInfo", "ChannelType", temp_channel_type);
channel_type = (LLTransferChannelType)temp_channel_type;
// See if it's a target that we're trying to abort
// Find the transfer associated with this packet.
LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
if (ttcp)
{
LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
if (ttp)
{
ttp->abortTransfer();
ttcp->deleteTransfer(ttp);
return;
}
}
// Hmm, not a target. Maybe it's a source.
LLTransferSourceChannel *tscp = gTransferManager.getSourceChannel(msgp->getSender(), channel_type);
if (tscp)
{
LLTransferSource *tsp = tscp->findTransferSource(transfer_id);
if (tsp)
{
tsp->abortTransfer();
tscp->deleteTransfer(tsp);
return;
}
}
LL_WARNS() << "Couldn't find transfer " << transfer_id << " to abort!" << LL_ENDL;
}
//static
void LLTransferManager::reliablePacketCallback(void **user_data, S32 result)
{
LLUUID *transfer_idp = (LLUUID *)user_data;
if (result &&
transfer_idp != NULL)
{
LLTransferSource *tsp = gTransferManager.findTransferSource(*transfer_idp);
if (tsp)
{
LL_WARNS() << "Aborting reliable transfer " << *transfer_idp << " due to failed reliable resends!" << LL_ENDL;
LLTransferSourceChannel *tscp = tsp->mChannelp;
tsp->abortTransfer();
tscp->deleteTransfer(tsp);
}
else
{
LL_WARNS() << "Aborting reliable transfer " << *transfer_idp << " but can't find the LLTransferSource object" << LL_ENDL;
}
}
delete transfer_idp;
}
//
// LLTransferConnection implementation
//
LLTransferConnection::LLTransferConnection(const LLHost &host)
{
mHost = host;
}
LLTransferConnection::~LLTransferConnection()
{
tsc_iter itersc;
for (itersc = mTransferSourceChannels.begin(); itersc != mTransferSourceChannels.end(); itersc++)
{
delete *itersc;
}
mTransferSourceChannels.clear();
ttc_iter itertc;
for (itertc = mTransferTargetChannels.begin(); itertc != mTransferTargetChannels.end(); itertc++)
{
delete *itertc;
}
mTransferTargetChannels.clear();
}
void LLTransferConnection::updateTransfers()
{
// Do stuff for source transfers (basically, send data out).
tsc_iter iter, cur;
iter = mTransferSourceChannels.begin();
while (iter !=mTransferSourceChannels.end())
{
cur = iter;
iter++;
(*cur)->updateTransfers();
}
// Do stuff for target transfers
// Primarily, we should be aborting transfers that are irredeemably broken
// (large packet gaps that don't appear to be getting filled in, most likely)
// Probably should NOT be doing timeouts for other things, as new priority scheme
// means that a high priority transfer COULD block a transfer for a long time.
}
LLTransferSourceChannel *LLTransferConnection::getSourceChannel(const LLTransferChannelType channel_type)
{
tsc_iter iter;
for (iter = mTransferSourceChannels.begin(); iter != mTransferSourceChannels.end(); iter++)
{
if ((*iter)->getChannelType() == channel_type)
{
return *iter;
}
}
LLTransferSourceChannel *tscp = new LLTransferSourceChannel(channel_type, mHost);
mTransferSourceChannels.push_back(tscp);
return tscp;
}
LLTransferTargetChannel *LLTransferConnection::getTargetChannel(const LLTransferChannelType channel_type)
{
ttc_iter iter;
for (iter = mTransferTargetChannels.begin(); iter != mTransferTargetChannels.end(); iter++)
{
if ((*iter)->getChannelType() == channel_type)
{
return *iter;
}
}
LLTransferTargetChannel *ttcp = new LLTransferTargetChannel(channel_type, mHost);
mTransferTargetChannels.push_back(ttcp);
return ttcp;
}
//
// LLTransferSourceChannel implementation
//
const S32 DEFAULT_PACKET_SIZE = 1000;
LLTransferSourceChannel::LLTransferSourceChannel(const LLTransferChannelType channel_type, const LLHost &host) :
mChannelType(channel_type),
mHost(host),
mTransferSources(LLTransferSource::sSetPriority, LLTransferSource::sGetPriority),
mThrottleID(TC_ASSET)
{
}
LLTransferSourceChannel::~LLTransferSourceChannel()
{
LLPriQueueMap<LLTransferSource*>::pqm_iter iter =
mTransferSources.mMap.begin();
LLPriQueueMap<LLTransferSource*>::pqm_iter end =
mTransferSources.mMap.end();
for (; iter != end; ++iter)
{
// Just kill off all of the transfers
(*iter).second->abortTransfer();
delete iter->second;
}
mTransferSources.mMap.clear();
}
void LLTransferSourceChannel::updatePriority(LLTransferSource *tsp, const F32 priority)
{
mTransferSources.reprioritize(priority, tsp);
}
void LLTransferSourceChannel::updateTransfers()
{
// Actually, this should do the following:
// Decide if we can actually send data.
// If so, update priorities so we know who gets to send it.
// Send data from the sources, while updating until we've sent our throttle allocation.
LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(getHost());
if (!cdp)
{
return;
}
if (cdp->isBlocked())
{
// *NOTE: We need to make sure that the throttle bits
// available gets reset.
// We DON'T want to send any packets if they're blocked, they'll just end up
// piling up on the other end.
//LL_WARNS() << "Blocking transfers due to blocked circuit for " << getHost() << LL_ENDL;
return;
}
const S32 throttle_id = mThrottleID;
LLThrottleGroup &tg = cdp->getThrottleGroup();
if (tg.checkOverflow(throttle_id, 0.f))
{
return;
}
LLPriQueueMap<LLTransferSource *>::pqm_iter iter, next;
BOOL done = FALSE;
for (iter = mTransferSources.mMap.begin(); (iter != mTransferSources.mMap.end()) && !done;)
{
//LL_INFOS() << "LLTransferSourceChannel::updateTransfers()" << LL_ENDL;
// Do stuff.
next = iter;
next++;
LLTransferSource *tsp = iter->second;
U8 *datap = NULL;
S32 data_size = 0;
BOOL delete_data = FALSE;
S32 packet_id = 0;
S32 sent_bytes = 0;
LLTSCode status = LLTS_OK;
// Get the packetID for the next packet that we're transferring.
packet_id = tsp->getNextPacketID();
status = tsp->dataCallback(packet_id, DEFAULT_PACKET_SIZE, &datap, data_size, delete_data);
if (status == LLTS_SKIP)
{
// We don't have any data, but we're not done, just go on.
// This will presumably be used for streaming or async transfers that
// are stalled waiting for data from another source.
iter=next;
continue;
}
LLUUID *cb_uuid = new LLUUID(tsp->getID());
LLUUID transaction_id = tsp->getID();
// Send the data now, even if it's an error.
// The status code will tell the other end what to do.
gMessageSystem->newMessage("TransferPacket");
gMessageSystem->nextBlock("TransferData");
gMessageSystem->addUUID("TransferID", tsp->getID());
gMessageSystem->addS32("ChannelType", getChannelType());
gMessageSystem->addS32("Packet", packet_id); // HACK! Need to put in a REAL packet id
gMessageSystem->addS32("Status", status);
gMessageSystem->addBinaryData("Data", datap, data_size);
sent_bytes = gMessageSystem->getCurrentSendTotal();
gMessageSystem->sendReliable(getHost(), LL_DEFAULT_RELIABLE_RETRIES, TRUE, F32Seconds(0.f),
LLTransferManager::reliablePacketCallback, (void**)cb_uuid);
// Do bookkeeping for the throttle
done = tg.throttleOverflow(throttle_id, sent_bytes*8.f);
gTransferManager.addTransferBitsOut(mChannelType, sent_bytes*8);
// Clean up our temporary data.
if (delete_data)
{
delete[] datap;
datap = NULL;
}
if (findTransferSource(transaction_id) == NULL)
{
//Warning! In the case of an aborted transfer, the sendReliable call above calls
//AbortTransfer which in turn calls deleteTransfer which means that somewhere way
//down the chain our current iter can get invalidated resulting in an infrequent
//sim crash. This check gets us to a valid transfer source in this event.
iter=next;
continue;
}
// Update the packet counter
tsp->setLastPacketID(packet_id);
switch (status)
{
case LLTS_OK:
// We're OK, don't need to do anything. Keep sending data.
break;
case LLTS_ERROR:
LL_WARNS() << "Error in transfer dataCallback!" << LL_ENDL;
// fall through
case LLTS_DONE:
// We need to clean up this transfer source.
//LL_INFOS() << "LLTransferSourceChannel::updateTransfers() " << tsp->getID() << " done" << LL_ENDL;
tsp->completionCallback(status);
delete tsp;
mTransferSources.mMap.erase(iter);
iter = next;
break;
default:
LL_ERRS() << "Unknown transfer error code!" << LL_ENDL;
}
// At this point, we should do priority adjustment (since some transfers like
// streaming transfers will adjust priority based on how much they've sent and time,
// but I'm not going to bother yet. - djs.
}
}
void LLTransferSourceChannel::addTransferSource(LLTransferSource *sourcep)
{
sourcep->mChannelp = this;
mTransferSources.push(sourcep->getPriority(), sourcep);
}
LLTransferSource *LLTransferSourceChannel::findTransferSource(const LLUUID &transfer_id)
{
LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++)
{
LLTransferSource *tsp = iter->second;
if (tsp->getID() == transfer_id)
{
return tsp;
}
}
return NULL;
}
void LLTransferSourceChannel::deleteTransfer(LLTransferSource *tsp)
{
if (tsp)
{
LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++)
{
if (iter->second == tsp)
{
delete tsp;
mTransferSources.mMap.erase(iter);
return;
}
}
LL_WARNS() << "Unable to find transfer source id "
<< tsp->getID()
<< " to delete!"
<< LL_ENDL;
}
}
//
// LLTransferTargetChannel implementation
//
LLTransferTargetChannel::LLTransferTargetChannel(const LLTransferChannelType channel_type, const LLHost &host) :
mChannelType(channel_type),
mHost(host)
{
}
LLTransferTargetChannel::~LLTransferTargetChannel()
{
tt_iter iter;
for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
{
// Abort all of the current transfers
(*iter)->abortTransfer();
delete *iter;
}
mTransferTargets.clear();
}
void LLTransferTargetChannel::requestTransfer(
const LLTransferSourceParams& source_params,
const LLTransferTargetParams& target_params,
const F32 priority)
{
LLUUID id;
id.generate();
LLTransferTarget* ttp = LLTransferTarget::createTarget(
target_params.getType(),
id,
source_params.getType());
if (!ttp)
{
LL_WARNS() << "LLTransferManager::requestTransfer aborting due to target creation failure!" << LL_ENDL;
return;
}
ttp->applyParams(target_params);
addTransferTarget(ttp);
sendTransferRequest(ttp, source_params, priority);
}
void LLTransferTargetChannel::sendTransferRequest(LLTransferTarget *targetp,
const LLTransferSourceParams ¶ms,
const F32 priority)
{
//
// Pack the message with data which explains how to get the source, and
// send it off to the source for this channel.
//
llassert(targetp);
llassert(targetp->getChannel() == this);
gMessageSystem->newMessage("TransferRequest");
gMessageSystem->nextBlock("TransferInfo");
gMessageSystem->addUUID("TransferID", targetp->getID());
gMessageSystem->addS32("SourceType", params.getType());
gMessageSystem->addS32("ChannelType", getChannelType());
gMessageSystem->addF32("Priority", priority);
U8 tmp[MAX_PARAMS_SIZE];
LLDataPackerBinaryBuffer dp(tmp, MAX_PARAMS_SIZE);
params.packParams(dp);
S32 len = dp.getCurrentSize();
gMessageSystem->addBinaryData("Params", tmp, len);
gMessageSystem->sendReliable(mHost);
}
void LLTransferTargetChannel::addTransferTarget(LLTransferTarget *targetp)
{
targetp->mChannelp = this;
mTransferTargets.push_back(targetp);
}
LLTransferTarget *LLTransferTargetChannel::findTransferTarget(const LLUUID &transfer_id)
{
tt_iter iter;
for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
{
LLTransferTarget *ttp = *iter;
if (ttp->getID() == transfer_id)
{
return ttp;
}
}
return NULL;
}
void LLTransferTargetChannel::deleteTransfer(LLTransferTarget *ttp)
{
if (ttp)
{
tt_iter iter;
for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
{
if (*iter == ttp)
{
delete ttp;
mTransferTargets.erase(iter);
return;
}
}
LL_WARNS() << "Unable to find transfer target id "
<< ttp->getID()
<< " to delete!"
<< LL_ENDL;
}
}
//
// LLTransferSource implementation
//
LLTransferSource::LLTransferSource(const LLTransferSourceType type,
const LLUUID &transfer_id,
const F32 priority) :
mType(type),
mID(transfer_id),
mChannelp(NULL),
mPriority(priority),
mSize(0),
mLastPacketID(-1)
{
setPriority(priority);
}
LLTransferSource::~LLTransferSource()
{
// No actual cleanup of the transfer is done here, this is purely for
// memory cleanup. The completionCallback is guaranteed to get called
// before this happens.
}
void LLTransferSource::sendTransferStatus(LLTSCode status)
{
gMessageSystem->newMessage("TransferInfo");
gMessageSystem->nextBlock("TransferInfo");
gMessageSystem->addUUID("TransferID", getID());
gMessageSystem->addS32("TargetType", LLTTT_UNKNOWN);
gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
gMessageSystem->addS32("Status", status);
gMessageSystem->addS32("Size", mSize);
U8 tmp[MAX_PARAMS_SIZE];
LLDataPackerBinaryBuffer dp(tmp, MAX_PARAMS_SIZE);
packParams(dp);
S32 len = dp.getCurrentSize();
gMessageSystem->addBinaryData("Params", tmp, len);
gMessageSystem->sendReliable(mChannelp->getHost());
// Abort if there was as asset system issue.
if (status != LLTS_OK)
{
completionCallback(status);
mChannelp->deleteTransfer(this);
}
}
// This should never be called directly, the transfer manager is responsible for
// aborting the transfer from the channel. I might want to rethink this in the
// future, though.
void LLTransferSource::abortTransfer()
{
// Send a message down, call the completion callback
LL_INFOS() << "LLTransferSource::Aborting transfer " << getID() << " to " << mChannelp->getHost() << LL_ENDL;
gMessageSystem->newMessage("TransferAbort");
gMessageSystem->nextBlock("TransferInfo");
gMessageSystem->addUUID("TransferID", getID());
gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
gMessageSystem->sendReliable(mChannelp->getHost());
completionCallback(LLTS_ABORT);
}
//static
void LLTransferSource::registerSourceType(const LLTransferSourceType stype, LLTransferSourceCreateFunc func)
{
if (sSourceCreateMap.count(stype))
{
// Disallow changing what class handles a source type
// Unclear when you would want to do this, and whether it would work.
LL_ERRS() << "Reregistering source type " << stype << LL_ENDL;
}
else
{
sSourceCreateMap[stype] = func;
}
}
//static
LLTransferSource *LLTransferSource::createSource(const LLTransferSourceType stype,
const LLUUID &id,
const F32 priority)
{
switch (stype)
{
// *NOTE: The source file transfer mechanism is highly insecure and could
// lead to easy exploitation of a server process.
// I have removed all uses of it from the codebase. Phoenix.
//
//case LLTST_FILE:
// return new LLTransferSourceFile(id, priority);
case LLTST_ASSET:
return new LLTransferSourceAsset(id, priority);
default:
{
if (!sSourceCreateMap.count(stype))
{
// Use the callback to create the source type if it's not there.
LL_WARNS() << "Unknown transfer source type: " << stype << LL_ENDL;
return NULL;
}
return (sSourceCreateMap[stype])(id, priority);
}
}
}
// static
void LLTransferSource::sSetPriority(LLTransferSource *&tsp, const F32 priority)
{
tsp->setPriority(priority);
}
// static
F32 LLTransferSource::sGetPriority(LLTransferSource *&tsp)
{
return tsp->getPriority();
}
//
// LLTransferPacket implementation
//
LLTransferPacket::LLTransferPacket(const S32 packet_id, const LLTSCode status, const U8 *datap, const S32 size) :
mPacketID(packet_id),
mStatus(status),
mDatap(NULL),
mSize(size)
{
if (size == 0)
{
return;
}
mDatap = new U8[size];
if (mDatap != NULL)
{
memcpy(mDatap, datap, size); /*Flawfinder: ignore*/
}
}
LLTransferPacket::~LLTransferPacket()
{
delete[] mDatap;
}
//
// LLTransferTarget implementation
//
LLTransferTarget::LLTransferTarget(
LLTransferTargetType type,
const LLUUID& transfer_id,
LLTransferSourceType source_type) :
mType(type),
mSourceType(source_type),
mID(transfer_id),
mChannelp(NULL),
mGotInfo(FALSE),
mSize(0),
mLastPacketID(-1)
{
}
LLTransferTarget::~LLTransferTarget()
{
// No actual cleanup of the transfer is done here, this is purely for
// memory cleanup. The completionCallback is guaranteed to get called
// before this happens.
tpm_iter iter;
for (iter = mDelayedPacketMap.begin(); iter != mDelayedPacketMap.end(); iter++)
{
delete iter->second;
}
mDelayedPacketMap.clear();
}
// This should never be called directly, the transfer manager is responsible for
// aborting the transfer from the channel. I might want to rethink this in the
// future, though.
void LLTransferTarget::abortTransfer()
{
// Send a message up, call the completion callback
LL_INFOS() << "LLTransferTarget::Aborting transfer " << getID() << " from " << mChannelp->getHost() << LL_ENDL;
gMessageSystem->newMessage("TransferAbort");
gMessageSystem->nextBlock("TransferInfo");
gMessageSystem->addUUID("TransferID", getID());
gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
gMessageSystem->sendReliable(mChannelp->getHost());
completionCallback(LLTS_ABORT);
}
bool LLTransferTarget::addDelayedPacket(
const S32 packet_id,
const LLTSCode status,
U8* datap,
const S32 size)
{
const transfer_packet_map::size_type LL_MAX_DELAYED_PACKETS = 100;
if(mDelayedPacketMap.size() > LL_MAX_DELAYED_PACKETS)
{
// too many delayed packets
return false;
}
LLTransferPacket* tpp = new LLTransferPacket(
packet_id,
status,
datap,
size);
#ifdef _DEBUG
transfer_packet_map::iterator iter = mDelayedPacketMap.find(packet_id);
if (iter != mDelayedPacketMap.end())
{
if (!(iter->second->mSize == size) && !(iter->second->mDatap == datap))
{
LL_ERRS() << "Packet ALREADY in delayed packet map!" << LL_ENDL;
}
}
#endif
mDelayedPacketMap[packet_id] = tpp;
return true;
}
LLTransferTarget* LLTransferTarget::createTarget(
LLTransferTargetType type,
const LLUUID& id,
LLTransferSourceType source_type)
{
switch (type)
{
case LLTTT_FILE:
return new LLTransferTargetFile(id, source_type);
case LLTTT_VFILE:
return new LLTransferTargetVFile(id, source_type);
default:
LL_WARNS() << "Unknown transfer target type: " << type << LL_ENDL;
return NULL;
}
}
LLTransferSourceParamsInvItem::LLTransferSourceParamsInvItem() : LLTransferSourceParams(LLTST_SIM_INV_ITEM), mAssetType(LLAssetType::AT_NONE)
{
}
void LLTransferSourceParamsInvItem::setAgentSession(const LLUUID &agent_id, const LLUUID &session_id)
{
mAgentID = agent_id;
mSessionID = session_id;
}
void LLTransferSourceParamsInvItem::setInvItem(const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id)
{
mOwnerID = owner_id;
mTaskID = task_id;
mItemID = item_id;
}
void LLTransferSourceParamsInvItem::setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type)
{
mAssetID = asset_id;
mAssetType = asset_type;
}
void LLTransferSourceParamsInvItem::packParams(LLDataPacker &dp) const
{
LL_DEBUGS() << "LLTransferSourceParamsInvItem::packParams()" << LL_ENDL;
dp.packUUID(mAgentID, "AgentID");
dp.packUUID(mSessionID, "SessionID");
dp.packUUID(mOwnerID, "OwnerID");
dp.packUUID(mTaskID, "TaskID");
dp.packUUID(mItemID, "ItemID");
dp.packUUID(mAssetID, "AssetID");
dp.packS32(mAssetType, "AssetType");
}
BOOL LLTransferSourceParamsInvItem::unpackParams(LLDataPacker &dp)
{
S32 tmp_at;
dp.unpackUUID(mAgentID, "AgentID");
dp.unpackUUID(mSessionID, "SessionID");
dp.unpackUUID(mOwnerID, "OwnerID");
dp.unpackUUID(mTaskID, "TaskID");
dp.unpackUUID(mItemID, "ItemID");
dp.unpackUUID(mAssetID, "AssetID");
dp.unpackS32(tmp_at, "AssetType");
mAssetType = (LLAssetType::EType)tmp_at;
return TRUE;
}
LLTransferSourceParamsEstate::LLTransferSourceParamsEstate() :
LLTransferSourceParams(LLTST_SIM_ESTATE),
mEstateAssetType(ET_NONE),
mAssetType(LLAssetType::AT_NONE)
{
}
void LLTransferSourceParamsEstate::setAgentSession(const LLUUID &agent_id, const LLUUID &session_id)
{
mAgentID = agent_id;
mSessionID = session_id;
}
void LLTransferSourceParamsEstate::setEstateAssetType(const EstateAssetType etype)
{
mEstateAssetType = etype;
}
void LLTransferSourceParamsEstate::setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type)
{
mAssetID = asset_id;
mAssetType = asset_type;
}
void LLTransferSourceParamsEstate::packParams(LLDataPacker &dp) const
{
dp.packUUID(mAgentID, "AgentID");
// *NOTE: We do not want to pass the session id from the server to
// the client, but I am not sure if anyone expects this value to
// be set on the client.
dp.packUUID(mSessionID, "SessionID");
dp.packS32(mEstateAssetType, "EstateAssetType");
}
BOOL LLTransferSourceParamsEstate::unpackParams(LLDataPacker &dp)
{
S32 tmp_et;
dp.unpackUUID(mAgentID, "AgentID");
dp.unpackUUID(mSessionID, "SessionID");
dp.unpackS32(tmp_et, "EstateAssetType");
mEstateAssetType = (EstateAssetType)tmp_et;
return TRUE;
}