summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2016-02-11 14:50:37 (GMT)
committerKevin Smith <kevin.smith@isode.com>2016-02-15 13:05:18 (GMT)
commit75703db2de5bbfb6622286600362016edb42dfb0 (patch)
tree2520ed777286c6b732e756387ca88d49b9c1814e /Swiften/QA/FileTransferTest/FileTransferTest.cpp
parentca226e7bb019308db4bfc818d7e04422d9d28106 (diff)
downloadswift-75703db2de5bbfb6622286600362016edb42dfb0.zip
swift-75703db2de5bbfb6622286600362016edb42dfb0.tar.bz2
Support early IBB use in Jingle File Transfer
Previously Jingle File Transfer in Swiften only used IBB transport as fallback mechanism. With this patch Swiften will use IBB transport candidates directly in the first session-initate/session-accept message if the other party only supports IBB. Fixed a ASAN reported heap-use-after-free in SOCKS5BytestreamServerManager.cpp while testing. Test-Information: ./scons test=system passed without error. Testing all sender/receiver file-transfer option configurations with FileTransferTest resulting in expected behavior. Successfully transferring a file between two Swift instances. Change-Id: Ia0ffeaa1fd54fc0da23db75344c9e94f9d03a774
Diffstat (limited to 'Swiften/QA/FileTransferTest/FileTransferTest.cpp')
-rw-r--r--Swiften/QA/FileTransferTest/FileTransferTest.cpp45
1 files changed, 34 insertions, 11 deletions
diff --git a/Swiften/QA/FileTransferTest/FileTransferTest.cpp b/Swiften/QA/FileTransferTest/FileTransferTest.cpp
index 8597033..7a50e9f 100644
--- a/Swiften/QA/FileTransferTest/FileTransferTest.cpp
+++ b/Swiften/QA/FileTransferTest/FileTransferTest.cpp
@@ -22,234 +22,256 @@
#include <Swiften/Elements/Presence.h>
#include <Swiften/EventLoop/SimpleEventLoop.h>
#include <Swiften/FileTransfer/FileReadBytestream.h>
#include <Swiften/FileTransfer/FileTransferManager.h>
#include <Swiften/FileTransfer/FileWriteBytestream.h>
#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
#include <Swiften/FileTransfer/ReadBytestream.h>
#include <Swiften/Network/BoostNetworkFactories.h>
#include <Swiften/Network/Timer.h>
#include <Swiften/Network/TimerFactory.h>
using namespace Swift;
static const std::string CLIENT_NAME = "Swiften FT Test";
static const std::string CLIENT_NODE = "http://swift.im";
static boost::shared_ptr<SimpleEventLoop> eventLoop;
static boost::shared_ptr<BoostNetworkFactories> networkFactories;
BoostRandomGenerator randGen;
enum Candidate {
InBandBytestream = 1,
S5B_Direct = 2,
S5B_Proxied = 4,
S5B_Assisted = 8,
};
class FileTransferTest {
public:
- FileTransferTest(int senderCandidates, int receiverCandidates) : senderCandidates_(senderCandidates), senderIsDone_(false), receiverCandidates_(receiverCandidates), receiverIsDone_(false) {
+ FileTransferTest(int senderCandidates, int receiverCandidates) : senderCandidates_(senderCandidates), senderError_(FileTransferError::UnknownError), senderIsDone_(false), receiverCandidates_(receiverCandidates), receiverError_(FileTransferError::UnknownError), receiverIsDone_(false) {
sender_ = boost::make_shared<Client>(JID(getenv("SWIFT_FILETRANSFERTEST_JID")), getenv("SWIFT_FILETRANSFERTEST_PASS"), networkFactories.get());
sender_->onDisconnected.connect(boost::bind(&FileTransferTest::handleSenderDisconnected, this, _1));
sender_->onConnected.connect(boost::bind(&FileTransferTest::handleSenderConnected, this));
sender_->getEntityCapsProvider()->onCapsChanged.connect(boost::bind(&FileTransferTest::handleSenderCapsChanged, this, _1));
receiver_ = boost::make_shared<Client>(JID(getenv("SWIFT_FILETRANSFERTEST2_JID")), getenv("SWIFT_FILETRANSFERTEST2_PASS"), networkFactories.get());
receiver_->onConnected.connect(boost::bind(&FileTransferTest::handleReceiverConnected, this));
receiver_->onDisconnected.connect(boost::bind(&FileTransferTest::handleReceiverDisconnected, this, _1));
senderTracer_ = new ClientXMLTracer(sender_.get());
receiverTracer_ = new ClientXMLTracer(receiver_.get());
ClientOptions options;
options.useTLS = ClientOptions::NeverUseTLS;
options.useStreamCompression = false;
options.useStreamResumption = false;
options.useAcks = false;
sender_->connect(options);
receiver_->connect(options);
timeOut_ = networkFactories->getTimerFactory()->createTimer(60000);
timeOut_->onTick.connect(boost::bind(&FileTransferTest::handleTimeOut, this));
// Create randomly sized data to exchange.
sendFilePath_ = boost::filesystem::unique_path("ft_send_%%%%%%%%%%%%%%%%.bin");
receiveFilePath_ = boost::filesystem::unique_path("ft_receive_%%%%%%%%%%%%%%%%.bin");
size_t size = 1024 + boost::numeric_cast<size_t>(randGen.generateRandomInteger(1024 * 10));
sendData_.resize(size);
for (size_t n = 0; n < sendData_.size(); n++) {
sendData_[n] = boost::numeric_cast<unsigned char>(randGen.generateRandomInteger(255));
}
std::ofstream outfile(sendFilePath_.native().c_str(), std::ios::out | std::ios::binary);
outfile.write(reinterpret_cast<char *>(&sendData_[0]), boost::numeric_cast<ptrdiff_t>(sendData_.size()));
outfile.close();
}
~FileTransferTest() {
timeOut_->stop();
delete senderTracer_;
delete receiverTracer_;
// Free file-transfer objects so file handles are closed and files can be removed afterwards.
assert(!outgoingFileTransfer_ && incomingFileTransfers_.empty());
if(boost::filesystem::exists(sendFilePath_)) {
boost::filesystem::remove(sendFilePath_);
}
if(boost::filesystem::exists(receiveFilePath_)) {
boost::filesystem::remove(receiveFilePath_);
}
}
void handleSenderConnected() {
+ DiscoInfo discoInfo;
+ discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc"));
+ discoInfo.addFeature(DiscoInfo::JingleFeature);
+ discoInfo.addFeature(DiscoInfo::JingleFTFeature);
+ discoInfo.addFeature(DiscoInfo::Bytestream);
+ if (senderCandidates_ & InBandBytestream) {
+ discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature);
+ }
+ if (senderCandidates_ & (S5B_Direct | S5B_Assisted | S5B_Proxied)) {
+ discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature);
+ }
+ sender_->getDiscoManager()->setCapsNode(CLIENT_NODE);
+ sender_->getDiscoManager()->setDiscoInfo(discoInfo);
sender_->sendPresence(Presence::create());
}
void handleReceiverConnected() {
receiver_->getFileTransferManager()->onIncomingFileTransfer.connect(boost::bind(&FileTransferTest::handleReceiverIncomingFileTransfer, this, _1));
DiscoInfo discoInfo;
discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc"));
discoInfo.addFeature(DiscoInfo::JingleFeature);
discoInfo.addFeature(DiscoInfo::JingleFTFeature);
discoInfo.addFeature(DiscoInfo::Bytestream);
- discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature);
- discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature);
+ if (receiverCandidates_ & InBandBytestream) {
+ discoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature);
+ }
+ if (receiverCandidates_ & (S5B_Direct | S5B_Assisted | S5B_Proxied)) {
+ discoInfo.addFeature(DiscoInfo::JingleTransportsS5BFeature);
+ }
receiver_->getDiscoManager()->setCapsNode(CLIENT_NODE);
receiver_->getDiscoManager()->setDiscoInfo(discoInfo);
receiver_->getPresenceSender()->sendPresence(Presence::create());
}
void handleReceiverIncomingFileTransfer(IncomingFileTransfer::ref transfer) {
incomingFileTransfers_.push_back(transfer);
boost::shared_ptr<FileWriteBytestream> out = boost::make_shared<FileWriteBytestream>(receiveFilePath_.native());
transfer->onFinished.connect(boost::bind(&FileTransferTest::handleReceiverFileTransferFinished, this, _1, out));
FileTransferOptions options;
options = options.withInBandAllowed(receiverCandidates_ & InBandBytestream);
options = options.withDirectAllowed(receiverCandidates_ & S5B_Direct);
options = options.withAssistedAllowed(receiverCandidates_ & S5B_Assisted);
options = options.withProxiedAllowed(receiverCandidates_ & S5B_Proxied);
std::cout << "Incoming transfer options: " << "IBB (" << options.isInBandAllowed() << ")" << ", ";
std::cout << "S5B Direct (" << options.isDirectAllowed() << ")" << ", ";
std::cout << "S5B Assisted (" << options.isAssistedAllowed() << ")" << ", ";
std::cout << "S5B Proxied (" << options.isProxiedAllowed() << ")" << std::endl;
transfer->accept(out, options);
}
void handleSenderCapsChanged(const JID &jid) {
if (receiver_ && (receiver_->getJID().toBare() == jid.toBare())) {
boost::shared_ptr<FileReadBytestream> fileStream = boost::make_shared<FileReadBytestream>(sendFilePath_);
FileTransferOptions options;
options = options.withInBandAllowed(senderCandidates_ & InBandBytestream);
options = options.withDirectAllowed(senderCandidates_ & S5B_Direct);
options = options.withAssistedAllowed(senderCandidates_ & S5B_Assisted);
options = options.withProxiedAllowed(senderCandidates_ & S5B_Proxied);
std::cout << "Outgoing transfer options: " << "IBB (" << options.isInBandAllowed() << ")" << ", ";
std::cout << "S5B Direct (" << options.isDirectAllowed() << ")" << ", ";
std::cout << "S5B Assisted (" << options.isAssistedAllowed() << ")" << ", ";
std::cout << "S5B Proxied (" << options.isProxiedAllowed() << ")" << std::endl;
outgoingFileTransfer_ = sender_->getFileTransferManager()->createOutgoingFileTransfer(jid.toBare(), sendFilePath_, "Some File!", fileStream, options);
if (outgoingFileTransfer_) {
outgoingFileTransfer_->onFinished.connect(boost::bind(&FileTransferTest::handleSenderFileTransferFinished, this, _1));
outgoingFileTransfer_->start();
} else {
std::cout << "ERROR: No outgoing file transfer returned." << std::endl;
+ receiverIsDone_ = true;
+ senderIsDone_ = true;
endTest();
}
}
}
void handleReceiverFileTransferFinished(const boost::optional<FileTransferError>& error, boost::shared_ptr<FileWriteBytestream> out) {
out->close();
receiverError_ = error;
receiverIsDone_ = true;
if (senderIsDone_) {
timeOut_->stop();
timeOut_ = networkFactories->getTimerFactory()->createTimer(1000);
timeOut_->onTick.connect(boost::bind(&FileTransferTest::endTest, this));
timeOut_->start();
}
}
void handleSenderDisconnected(const boost::optional<ClientError>& error) {
if (error) {
std::cout << this << " " << "handleSenderDisconnected: error: " << error.get() << std::endl;
}
// All file-transfers related to a Client instance need to be freed
// *before* freeing the Client instance.
outgoingFileTransfer_.reset();
sender_.reset();
if (!sender_ && !receiver_) {
eventLoop->stop();
}
}
void handleReceiverDisconnected(const boost::optional<ClientError>& error) {
if (error) {
std::cout << this << " " << "handleReceiverDisconnected: error: " << error.get() << std::endl;
}
// All file-transfers related to a Client instance need to be freed
// *before* freeing the Client instance.
incomingFileTransfers_.clear();
receiver_.reset();
if (!sender_ && !receiver_) {
eventLoop->stop();
}
}
void handleSenderFileTransferFinished(const boost::optional<FileTransferError>& error) {
senderError_ = error;
senderIsDone_ = true;
if (receiverIsDone_) {
timeOut_->stop();
timeOut_ = networkFactories->getTimerFactory()->createTimer(1000);
timeOut_->onTick.connect(boost::bind(&FileTransferTest::endTest, this));
timeOut_->start();
}
+ else if (error) {
+ endTest();
+ }
}
void run() {
timeOut_->start();
eventLoop->run();
}
void endTest() {
if (sender_) {
sender_->disconnect();
}
if (receiver_) {
receiver_->disconnect();
}
}
void handleTimeOut() {
std::cout << "Test timed out!!!" << std::endl;
endTest();
}
bool isDone() const {
return senderIsDone_ && receiverIsDone_;
}
bool wasSuccessful() const {
return !senderError_ && !receiverError_;
}
private:
@@ -260,70 +282,71 @@ class FileTransferTest {
OutgoingFileTransfer::ref outgoingFileTransfer_;
boost::filesystem::path sendFilePath_;
boost::optional<FileTransferError> senderError_;
bool senderIsDone_;
int receiverCandidates_;
boost::shared_ptr<Client> receiver_;
ClientXMLTracer* receiverTracer_;
ByteArray receiveData_;
std::vector<IncomingFileTransfer::ref> incomingFileTransfers_;
boost::filesystem::path receiveFilePath_;
boost::optional<FileTransferError> receiverError_;
bool receiverIsDone_;
Timer::ref timeOut_;
};
static bool runTest(int senderCandidates, int receiverCandidates) {
bool success = false;
std::cout << "senderCandidates: " << senderCandidates << ", receiverCandidates: " << receiverCandidates << std::endl;
bool expectSuccess = (senderCandidates & receiverCandidates) > 0;
eventLoop = boost::make_shared<SimpleEventLoop>();
networkFactories = boost::make_shared<BoostNetworkFactories>(eventLoop.get());
boost::shared_ptr<FileTransferTest> testRun = boost::make_shared<FileTransferTest>(senderCandidates, receiverCandidates);
testRun->run();
- if (testRun->isDone()) {
- bool wasSuccessful = testRun->wasSuccessful();
- if (expectSuccess == wasSuccessful) {
- success = true;
- } else {
- std::cout << "expected success: " << expectSuccess << ", wasSuccessful: " << wasSuccessful << std::endl;
+ bool wasSuccessful = testRun->wasSuccessful();
+ if (expectSuccess == wasSuccessful) {
+ success = true;
+ }
+ else {
+ if (!testRun->isDone()) {
+ std::cout << "Test did not finish transfer. Sender candidates = " << senderCandidates << ", receiver candidates = " << receiverCandidates << "." << std::endl;
}
- } else {
- std::cout << "Failed to run test! Sender candidates = " << senderCandidates << ", receiver candidates = " << receiverCandidates << "." << std::endl;
}
+ std::cout << "expected success: " << expectSuccess << ", wasSuccessful: " << wasSuccessful << std::endl;
+
testRun.reset();
networkFactories.reset();
eventLoop->runUntilEvents();
eventLoop->stop();
eventLoop.reset();
return success;
}
/**
* This program test file-transfer interop between Swift and itself with various connection candidates.
* The all combinations of the candidates, IBB, S5B (direct) and S5B (proxied), on sender and receiver side are tested.
*/
int main(int argc, char** argv) {
int failedTests = 0;
std::vector<std::pair<int, int> > failedTestPairs;
std::cout << "Swiften File-Transfer Connectivity Test Suite" << std::endl;
if (argc == 1) {
if (getenv("SWIFT_FILETRANSFERTEST_CONFIG")) {
// test configuration described in SWIFT_FILETRANSFERTEST_CONFIG environment variable, e.g. "1:1|2:2"
std::vector<std::string> configurations;
std::string configs_env = std::string(getenv("SWIFT_FILETRANSFERTEST_CONFIG"));
boost::split(configurations, configs_env, boost::is_any_of("|"));
foreach(const std::string& config, configurations) {
std::vector<std::string> split_config;
boost::split(split_config, config, boost::is_any_of(":"));
assert(split_config.size() == 2);