Patch by Jerry:

- Fixed line endings
- Integ test for file transfer
- Fixed crashed problem when log info is larger than 2048 bytes
- Fixed compile error caused by std exception (by Feng ye)
- Fixed include path on Mac and linux (by Feng ye)
This commit is contained in:
Nick Bolton 2013-07-24 16:41:12 +00:00
parent c368013f13
commit 394ece004a
72 changed files with 3884 additions and 3127 deletions

View File

@ -178,7 +178,7 @@ CLog::print(const char* file, int line, const char* fmt, ...)
// do not prefix time and file for kPRINT (CLOG_PRINT)
if (priority != kPRINT) {
char message[2048];
char message[kLogMessageLength];
#ifndef NDEBUG
struct tm *tm;

View File

@ -137,6 +137,8 @@ private:
int m_maxPriority;
};
const UInt16 kLogMessageLength = 2048;
/*!
\def LOG(arg)
Write to the log. Because macros cannot accept variable arguments, this

View File

@ -33,14 +33,20 @@
#include "CArch.h"
#include "IPlatformScreen.h"
#include "CCryptoStream.h"
#include "CThread.h"
#include "TMethodJob.h"
#include "CFileChunker.h"
#include <cstring>
#include <cstdlib>
#include <sstream>
#include <fstream>
//
// CClient
//
const size_t CClient::m_chunkSize = 1024 * 512; // 512kb
CClient::CClient(IEventQueue* events,
const CString& name, const CNetworkAddress& address,
ISocketFactory* socketFactory,
@ -438,6 +444,17 @@ CClient::sendConnectionFailedEvent(const char* msg)
m_events->addEvent(event);
}
void
CClient::sendFileChunk(const void* data)
{
CFileChunker::CFileChunk* fileChunk = reinterpret_cast<CFileChunker::CFileChunk*>(const_cast<void*>(data));
LOG((CLOG_DEBUG1 "sendFileChunk"));
assert(m_server != NULL);
// relay
m_server->fileChunkSending(fileChunk->m_chunk[0], &(fileChunk->m_chunk[1]), fileChunk->m_dataSize);
}
void
CClient::setupConnecting()
{
@ -737,6 +754,7 @@ CClient::handleGameDeviceFeedback(const CEvent& event, void*)
void
CClient::handleFileChunkSending(const CEvent& event, void*)
{
sendFileChunk(event.getData());
}
void
@ -763,3 +781,24 @@ CClient::isReceivedFileSizeValid()
{
return m_expectedFileSize == m_receivedFileData.size();
}
void
CClient::sendFileToServer(const char* filename)
{
CThread* thread = new CThread(
new TMethodJob<CClient>(
this, &CClient::sendFileThread,
reinterpret_cast<void*>(const_cast<char*>(filename))));
}
void
CClient::sendFileThread(void* filename)
{
try {
char* name = reinterpret_cast<char*>(filename);
CFileChunker::sendFileChunks(name, m_events, this);
}
catch (std::runtime_error error) {
LOG((CLOG_ERR "failed sending file chunks: %s", error.what()));
}
}

View File

@ -101,8 +101,8 @@ public:
//! Received a chunk of file data
void fileChunkReceived(CString data);
//! Return true if recieved file size is valid
bool isReceivedFileSizeValid();
//! Create a new thread and use it to send file to Server
void sendFileToServer(const char* filename);
//@}
//! @name accessors
@ -128,6 +128,9 @@ public:
*/
CNetworkAddress getServerAddress() const;
//! Return true if recieved file size is valid
bool isReceivedFileSizeValid();
//@}
// IScreen overrides
@ -167,6 +170,8 @@ private:
void sendClipboard(ClipboardID);
void sendEvent(CEvent::Type, void*);
void sendConnectionFailedEvent(const char* msg);
void sendFileChunk(const void* data);
void sendFileThread(void*);
void setupConnecting();
void setupConnection();
void setupScreen();
@ -214,6 +219,7 @@ private:
CCryptoOptions m_crypto;
std::size_t m_expectedFileSize;
CString m_receivedFileData;
static const size_t m_chunkSize;
};
#endif

View File

@ -935,7 +935,8 @@ CServerProxy::infoAcknowledgment()
m_ignoreMouse = false;
}
void CServerProxy::fileChunkReceived()
void
CServerProxy::fileChunkReceived()
{
// parse
UInt8 mark;
@ -943,20 +944,42 @@ void CServerProxy::fileChunkReceived()
CProtocolUtil::readf(m_stream, kMsgDFileTransfer + 4, &mark, &content);
switch (mark) {
case '0':
LOG((CLOG_DEBUG2 "recv file data: file size = %s", content));
case kFileStart:
LOG((CLOG_DEBUG2 "recv file data from server: size=%s", content.c_str()));
m_client->clearReceivedFileData();
m_client->setExpectedFileSize(content);
break;
case '1':
LOG((CLOG_DEBUG2 "recv file data: chunck size = %i", content.size()));
case kFileChunk:
LOG((CLOG_DEBUG2 "recv file data from server: size=%i", content.size()));
m_client->fileChunkReceived(content);
break;
case '2':
case kFileEnd:
LOG((CLOG_DEBUG2 "file data transfer finished"));
m_events->addEvent(CEvent(m_events->forIScreen().fileRecieveComplete(), m_client));
break;
}
}
void
CServerProxy::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
{
CString chunk(data, dataSize);
switch (mark) {
case kFileStart:
LOG((CLOG_DEBUG2 "file sending start: size=%s", data));
break;
case kFileChunk:
LOG((CLOG_DEBUG2 "file chunk sending: size=%i", chunk.size()));
break;
case kFileEnd:
LOG((CLOG_DEBUG2 "file sending finished"));
break;
}
CProtocolUtil::writef(m_stream, kMsgDFileTransfer, mark, &chunk);
}

View File

@ -56,6 +56,14 @@ public:
//@}
//! @file transfer
//@{
//! sending file chunk to server
void fileChunkSending(UInt8 mark, char* data, size_t dataSize);
//@}
#ifdef TEST_ENV
void handleDataForTest() { handleData(CEvent(), NULL); }
#endif

View File

@ -83,7 +83,7 @@ public:
virtual void screensaver(bool activate) = 0;
virtual void resetOptions() = 0;
virtual void setOptions(const COptionsList& options) = 0;
virtual void fileChunkSending(UInt8 mark, const UInt8* data) = 0;
virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize) = 0;
virtual CString getName() const;
private:

View File

@ -89,7 +89,7 @@ public:
virtual void gameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2) = 0;
virtual void gameDeviceTimingReq() = 0;
virtual void cryptoIv(const UInt8* iv) = 0;
virtual void fileChunkSending(UInt8 mark, const UInt8* data) = 0;
virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize) = 0;
private:
synergy::IStream* m_stream;

View File

@ -394,7 +394,7 @@ CClientProxy1_0::cryptoIv(const UInt8* iv)
}
void
CClientProxy1_0::fileChunkSending(UInt8 mark, const UInt8* iv)
CClientProxy1_0::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
{
// ignore -- not supported in protocol 1.0
LOG((CLOG_DEBUG "fileChunkSending not supported"));

View File

@ -64,7 +64,7 @@ public:
virtual void gameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2);
virtual void gameDeviceTimingReq();
virtual void cryptoIv(const UInt8* iv);
virtual void fileChunkSending(UInt8 mark, const UInt8* data);
virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize);
protected:
virtual bool parseHandshakeMessage(const UInt8* code);

View File

@ -42,7 +42,6 @@ protected:
private:
void handleKeepAlive(const CEvent&, void*);
private:
double m_keepAliveRate;
CEventQueueTimer* m_keepAliveTimer;

View File

@ -29,6 +29,14 @@ public:
CClientProxy1_4(const CString& name, synergy::IStream* adoptedStream, CServer* server, IEventQueue* events);
~CClientProxy1_4();
//! @name accessors
//@{
//! get server pointer
CServer* getServer() { return m_server; }
//@}
// IClient overrides
virtual void gameDeviceButtons(GameDeviceID id, GameDeviceButton buttons);
virtual void gameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2);

View File

@ -19,13 +19,15 @@
#include "CProtocolUtil.h"
#include "CLog.h"
#include "IStream.h"
#include "CServer.h"
//
// CClientProxy1_5
//
CClientProxy1_5::CClientProxy1_5(const CString& name, synergy::IStream* stream, CServer* server, IEventQueue* events) :
CClientProxy1_4(name, stream, server, events)
CClientProxy1_4(name, stream, server, events),
m_events(events)
{
}
@ -34,23 +36,64 @@ CClientProxy1_5::~CClientProxy1_5()
}
void
CClientProxy1_5::fileChunkSending(UInt8 mark, const UInt8* data)
CClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
{
CString chunk(reinterpret_cast<const char*>(data));
CString chunk(data, dataSize);
switch (mark) {
case '0':
LOG((CLOG_DEBUG2 "file sending start: file size = %s", data));
case kFileStart:
LOG((CLOG_DEBUG2 "file sending start: size=%s", data));
break;
case '1':
LOG((CLOG_DEBUG2 "file chunk sending: %s", data));
case kFileChunk:
LOG((CLOG_DEBUG2 "file chunk sending: size=%i", chunk.size()));
break;
case '2':
case kFileEnd:
LOG((CLOG_DEBUG2 "file sending finished"));
break;
}
CProtocolUtil::writef(getStream(), kMsgDFileTransfer, mark, &chunk);
}
bool
CClientProxy1_5::parseMessage(const UInt8* code)
{
if (memcmp(code, kMsgDFileTransfer, 4) == 0) {
fileChunkReceived();
}
else {
return CClientProxy1_4::parseMessage(code);
}
return true;
}
void
CClientProxy1_5::fileChunkReceived()
{
// parse
UInt8 mark;
CString content;
CProtocolUtil::readf(getStream(), kMsgDFileTransfer + 4, &mark, &content);
CServer* server = getServer();
switch (mark) {
case kFileStart:
LOG((CLOG_DEBUG2 "recv file data from client: file size=%s", content.c_str()));
server->clearReceivedFileData();
server->setExpectedFileSize(content);
break;
case kFileChunk:
LOG((CLOG_DEBUG2 "recv file data from client: chunck size=%i", content.size()));
server->fileChunkReceived(content);
break;
case kFileEnd:
LOG((CLOG_DEBUG2 "file data transfer finished"));
m_events->addEvent(CEvent(m_events->forIScreen().fileRecieveComplete(), server));
break;
}
}

View File

@ -28,5 +28,10 @@ public:
CClientProxy1_5(const CString& name, synergy::IStream* adoptedStream, CServer* server, IEventQueue* events);
~CClientProxy1_5();
virtual void fileChunkSending(UInt8 mark, const UInt8* data);
virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize);
virtual bool parseMessage(const UInt8* code);
void fileChunkReceived();
private:
IEventQueue* m_events;
};

View File

@ -274,7 +274,7 @@ CPrimaryClient::screensaver(bool)
}
void
CPrimaryClient::fileChunkSending(UInt8 mark, const UInt8* data)
CPrimaryClient::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
{
// ignore
}

View File

@ -148,7 +148,7 @@ public:
virtual void screensaver(bool activate);
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual void fileChunkSending(UInt8 mark, const UInt8* data);
virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize);
private:
CScreen* m_screen;

View File

@ -33,14 +33,22 @@
#include "TMethodEventJob.h"
#include "CArch.h"
#include "CKeyState.h"
#include "CScreen.h"
#include "CThread.h"
#include "TMethodJob.h"
#include "CFileChunker.h"
#include <cstring>
#include <cstdlib>
#include "CScreen.h"
#include <sstream>
#include <fstream>
#include <sstream>
//
// CServer
//
const size_t CServer::m_chunkSize = 1024 * 512; // 512kb
CServer::CServer(CConfig& config, CPrimaryClient* primaryClient, CScreen* screen, IEventQueue* events) :
m_events(events),
m_mock(false),
@ -177,6 +185,10 @@ CServer::CServer(CConfig& config, CPrimaryClient* primaryClient, CScreen* screen
this,
new TMethodEventJob<CServer>(this,
&CServer::handleFileChunkSendingEvent));
m_events->adoptHandler(m_events->forIScreen().fileRecieveComplete(),
this,
new TMethodEventJob<CServer>(this,
&CServer::handleFileRecieveCompleteEvent));
// add connection
addClient(m_primaryClient);
@ -1516,8 +1528,13 @@ CServer::handleFakeInputEndEvent(const CEvent&, void*)
void
CServer::handleFileChunkSendingEvent(const CEvent& event, void*)
{
UInt8* data = reinterpret_cast<UInt8*>(event.getData());
onFileChunkSending(data);
onFileChunkSending(event.getData());
}
void
CServer::handleFileRecieveCompleteEvent(const CEvent& event, void*)
{
onFileRecieveComplete();
}
void
@ -1981,13 +1998,32 @@ CServer::onGameDeviceTimingReq()
}
void
CServer::onFileChunkSending(const UInt8* data)
CServer::onFileChunkSending(const void* data)
{
CFileChunker::CFileChunk* fileChunk = reinterpret_cast<CFileChunker::CFileChunk*>(const_cast<void*>(data));
LOG((CLOG_DEBUG1 "onFileChunkSending"));
assert(m_active != NULL);
// relay
m_active->fileChunkSending(data[0], &data[1]);
m_active->fileChunkSending(fileChunk->m_chunk[0], &(fileChunk->m_chunk[1]), fileChunk->m_dataSize);
}
void
CServer::onFileRecieveComplete()
{
if (isReceivedFileSizeValid()) {
if (!m_fileTransferDes.empty()) {
std::fstream file;
file.open(m_fileTransferDes.c_str(), std::ios::out | std::ios::binary);
if (!file.is_open()) {
// TODO: file open failed
}
file.write(m_receivedFileData.c_str(), m_receivedFileData.size());
file.close();
}
}
}
bool
@ -2260,3 +2296,49 @@ CServer::CKeyboardBroadcastInfo::alloc(State state, const CString& screens)
strcpy(info->m_screens, screens.c_str());
return info;
}
void
CServer::clearReceivedFileData()
{
m_receivedFileData.clear();
}
void
CServer::setExpectedFileSize(CString data)
{
std::istringstream iss(data);
iss >> m_expectedFileSize;
}
void
CServer::fileChunkReceived(CString data)
{
m_receivedFileData += data;
}
bool
CServer::isReceivedFileSizeValid()
{
return m_expectedFileSize == m_receivedFileData.size();
}
void
CServer::sendFileToClient(const char* filename)
{
CThread* thread = new CThread(
new TMethodJob<CServer>(
this, &CServer::sendFileThread,
reinterpret_cast<void*>(const_cast<char*>(filename))));
}
void
CServer::sendFileThread(void* filename)
{
try {
char* name = reinterpret_cast<char*>(filename);
CFileChunker::sendFileChunks(name, m_events, this);
}
catch (std::runtime_error error) {
LOG((CLOG_ERR "failed sending file chunks: %s", error.what()));
}
}

View File

@ -144,6 +144,21 @@ public:
//! Notify of game device feedback
void gameDeviceFeedback(GameDeviceID id, UInt16 m1, UInt16 m2);
//! Clears the file buffer
void clearReceivedFileData();
//! Set the expected size of receiving file
void setExpectedFileSize(CString data);
//! Set
void setFileTransferDes(CString& des) { m_fileTransferDes = des; }
//! Received a chunk of file data
void fileChunkReceived(CString data);
//! Create a new thread and use it to send file to client
void sendFileToClient(const char* filename);
//@}
//! @name accessors
//@{
@ -160,6 +175,9 @@ public:
*/
void getClients(std::vector<CString>& list) const;
//! Return true if recieved file size is valid
bool isReceivedFileSizeValid();
//@}
private:
@ -299,6 +317,7 @@ private:
void handleFakeInputBeginEvent(const CEvent&, void*);
void handleFakeInputEndEvent(const CEvent&, void*);
void handleFileChunkSendingEvent(const CEvent&, void*);
void handleFileRecieveCompleteEvent(const CEvent&, void*);
// event processing
void onClipboardChanged(CBaseClientProxy* sender,
@ -318,7 +337,8 @@ private:
void onGameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2);
void onGameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2);
void onGameDeviceTimingReq();
void onFileChunkSending(const UInt8* data);
void onFileChunkSending(const void* data);
void onFileRecieveComplete();
// add client to list and attach event handlers for client
bool addClient(CBaseClientProxy*);
@ -343,6 +363,9 @@ private:
// force the cursor off of \p client
void forceLeaveClient(CBaseClientProxy* client);
// thread funciton for sending file
void sendFileThread(void*);
public:
bool m_mock;
@ -438,6 +461,13 @@ private:
CScreen* m_screen;
IEventQueue* m_events;
// file transfer
size_t m_expectedFileSize;
CString m_receivedFileData;
static const size_t m_chunkSize;
CString m_fileTransferSrc;
CString m_fileTransferDes;
};
#endif

View File

@ -171,6 +171,14 @@ CApp::parseArg(const int& argc, const char* const* argv, int& i)
argsBase().m_crypto.setMode(argv[++i]);
}
else if (isArg(i, argc, argv, NULL, "--filetransfer-src")) {
m_fileTransferSrc = argv[++i];
}
else if (isArg(i, argc, argv, NULL, "--filetransfer-des")) {
m_fileTransferDes = argv[++i];
}
else {
// option not supported here
return false;

View File

@ -101,6 +101,9 @@ public:
void setSocketMultiplexer(CSocketMultiplexer* sm) { m_socketMultiplexer = sm; }
CSocketMultiplexer* getSocketMultiplexer() const { return m_socketMultiplexer; }
CString& getFileTransferSrc() { return m_fileTransferSrc; }
CString& getFileTransferDes() { return m_fileTransferDes; }
private:
void handleIpcMessage(const CEvent&, void*);
@ -122,6 +125,8 @@ private:
CIpcClient* m_ipcClient;
IEventQueue* m_events;
CSocketMultiplexer* m_socketMultiplexer;
CString m_fileTransferSrc;
CString m_fileTransferDes;
};
#define BYE "\nTry `%s --help' for more information."

View File

@ -349,6 +349,14 @@ CClientApp::handleClientConnected(const CEvent&, void*)
LOG((CLOG_NOTE "connected to server"));
resetRestartTimeout();
updateStatus();
/*
// TODO: remove testing code for relase
CString fileFullDir = getFileTransferSrc();
if (!fileFullDir.empty()) {
s_client->sendFileToServer(getFileTransferSrc().c_str());
}
*/
}
@ -473,6 +481,7 @@ CClientApp::startClient()
#endif
s_client->connect();
updateStatus();
return true;
}

View File

@ -0,0 +1,100 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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, see <http://www.gnu.org/licenses/>.
*/
#include "CFileChunker.h"
#include "BasicTypes.h"
#include "ProtocolTypes.h"
#include "CEvent.h"
#include "IEventQueue.h"
#include "CEventTypes.h"
#include "CLOG.h"
#include <fstream>
#include <sstream>
using namespace std;
const size_t CFileChunker::m_chunkSize = 512 * 1024; // 512kb
void
CFileChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarget)
{
std::fstream file(reinterpret_cast<char*>(filename), std::ios::in | std::ios::binary);
if (!file.is_open()) {
throw runtime_error("failed to open file");
}
// check file size
file.seekg (0, std::ios::end);
size_t size = (size_t)file.tellg();
// send first message (file size)
CString fileSize = intToString(size);
UInt32 sizeLength = fileSize.size();
CFileChunk* sizeMessage = new CFileChunk(sizeLength + 2);
char* chunkData = sizeMessage->m_chunk;
chunkData[0] = kFileStart;
memcpy(&chunkData[1], fileSize.c_str(), sizeLength);
chunkData[sizeLength + 1] = '\0';
events->addEvent(CEvent(events->forIScreen().fileChunkSending(), eventTarget, sizeMessage));
// send chunk messages with a fixed chunk size
size_t sentLength = 0;
size_t chunkSize = m_chunkSize;
file.seekg (0, std::ios::beg);
while (true) {
// make sure we don't read too much from the mock data.
if (sentLength + chunkSize > size) {
chunkSize = size - sentLength;
}
// for fileChunk->m_chunk, the first byte is the chunk mark, last is \0
CFileChunk* fileChunk = new CFileChunk(chunkSize + 2);
char* chunkData = fileChunk->m_chunk;
chunkData[0] = kFileChunk;
file.read(&chunkData[1], chunkSize);
chunkData[chunkSize + 1] = '\0';
events->addEvent(CEvent(events->forIScreen().fileChunkSending(), eventTarget, fileChunk));
sentLength += chunkSize;
file.seekg (sentLength, std::ios::beg);
if (sentLength == size) {
break;
}
}
// send last message
CFileChunk* transferFinished = new CFileChunk(2);
chunkData = transferFinished->m_chunk;
chunkData[0] = kFileEnd;
chunkData[1] = '\0';
events->addEvent(CEvent(events->forIScreen().fileChunkSending(), eventTarget, transferFinished));
file.close();
}
CString
CFileChunker::intToString(size_t i)
{
stringstream ss;
ss << i;
return ss.str();
}

View File

@ -0,0 +1,46 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Bolton Software Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "CString.h"
class IEventQueue;
class CFileChunker {
public:
//! FileChunk data
class CFileChunk {
public:
CFileChunk(size_t chunkSize) : m_dataSize(chunkSize - 2)
{
m_chunk = new char[chunkSize];
}
~CFileChunk() { delete[] m_chunk; }
public:
const size_t m_dataSize;
char* m_chunk;
};
static void sendFileChunks(char* filename, IEventQueue* events, void* eventTarget);
static CString intToString(size_t i);
private:
static const size_t m_chunkSize;
};

View File

@ -49,6 +49,7 @@ set(inc
CArgsBase.h
IAppUtil.h
CEventGameDevice.h
CFileChunker.h
)
set(src
@ -77,6 +78,7 @@ set(src
CArgsBase.cpp
CEventGameDevice.cpp
CGameDevice.cpp
CFileChunker.cpp
)
if (WIN32)

View File

@ -228,7 +228,12 @@ CProtocolUtil::vreadf(synergy::IStream* stream, const char* fmt, va_list args)
}
throw;
}
// don't cause buffer overrun, using +100 chars in case
// someone modifies this log message in future.
if (len + 100 < kLogMessageLength) {
LOG((CLOG_DEBUG2 "readf: read %d byte string: %.*s", len, len, sBuffer));
}
// save the data
CString* dst = va_arg(args, CString*);

View File

@ -688,7 +688,7 @@ CServer*
CServerApp::openServer(CConfig& config, CPrimaryClient* primaryClient)
{
CServer* server = new CServer(config, primaryClient, s_serverScreen, m_events);
server->setFileTransferDes(getFileTransferDes());
try {
m_events->adoptHandler(
m_events->forCServer().disconnected(), server,

View File

@ -68,6 +68,13 @@ enum EDirectionMask {
kBottomMask = 1 << kBottom
};
// file transfer constants
enum EFileTransfer {
kFileStart = 1,
kFileChunk = 2,
kFileEnd = 3
};
//
// message codes (trailing NUL is not part of code). in comments, $n

View File

@ -19,6 +19,7 @@
#include "CLog.h"
#include "TMethodEventJob.h"
#include "CSimpleEventQueueBuffer.h"
#include <stdexcept>
void
CTestEventQueue::raiseQuitEvent()
@ -47,6 +48,5 @@ CTestEventQueue::cleanupQuitTimeout()
void
CTestEventQueue::handleQuitTimeout(const CEvent&, void* vclient)
{
LOG((CLOG_ERR "timeout"));
raiseQuitEvent();
throw std::runtime_error("test event queue timeout");
}

View File

@ -16,8 +16,11 @@
*/
#include <gtest/gtest.h>
#include <iostream>
#include <stdexcept>
#include <sstream>
#include <fstream>
#include <iostream>
#include <stdio.h>
#define TEST_ENV
@ -33,13 +36,14 @@
#include "CTCPSocketFactory.h"
#include "CCryptoOptions.h"
#include "CSocketMultiplexer.h"
#include "CMSWindowsScreen.h"
#include "CGameDevice.h"
#include "CThread.h"
#include "TMethodJob.h"
#include "CTestEventQueue.h"
#include "server/CMockInputFilter.h"
#include "TMethodJob.h"
#include "CThread.h"
#include "CFileChunker.h"
using namespace std;
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
@ -48,27 +52,58 @@ using ::testing::Invoke;
#define TEST_PORT 24803
#define TEST_HOST "localhost"
const int klargeDataSize = 512;
char g_largeData[klargeDataSize] = "large data:head.1221412312341244213123fdsfasdawdwadwadacwdd.12321412312341244213123fdsfasdawdwadwadacwdawddawdwacawdawd232141231awddawdwacawdawd2321412312341244213123fdsfasdawdwadacwdawddawdwacawdtrtetawdawdwaewe1213412321412312341244213123fdsfasdawdwadacwdawddawdwacawdawdawdwaewe121341awdwaewedacwdawddawdwacawdawd2321412312341244213123fdsfasdawdwadacwdawddawdwacawdtrtetawdawdwaewe1213412321412312341244213123fdsfasdawdwadacwdawddawdwacawdawdawdwaewe121341awdwaewe12134123njk1u31i2nm3e123hu23oi132213njk.tail";
const size_t kMockDataSize = 1024 * 1024 * 10; // 10MB
const UInt16 kMockDataChunkIncrement = 1024; // 1KB
const char* kMockFilename = "NetworkTests.mock";
const size_t kMockFileSize = 1024 * 1024 * 10; // 10MB
void sendFileToClient_getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h);
void sendFileToClient_getCursorPos(SInt32& x, SInt32& y);
void getScreenShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h);
void getCursorPos(SInt32& x, SInt32& y);
CString intToString(size_t i);
UInt8* newMockData(size_t size);
void createFile(fstream& file, const char* filename, size_t size);
class NetworkTests : public ::testing::Test
{
public:
NetworkTests() { }
NetworkTests() :
m_mockData(NULL),
m_mockDataSize(0),
m_mockFileSize(0)
{
m_mockData = newMockData(kMockDataSize);
createFile(m_mockFile, kMockFilename, kMockFileSize);
}
void sendData(CServer* server);
~NetworkTests()
{
remove(kMockFilename);
delete[] m_mockData;
}
void sendFileToClient_handleClientConnected(const CEvent&, void* vlistener);
void sendFileToClient_fileRecieveComplete(const CEvent&, void*);
void sendMockData(void* eventTarget);
void sendToClient_mockData_handleClientConnected(const CEvent&, void* vlistener);
void sendToClient_mockData_fileRecieveComplete(const CEvent&, void*);
void sendToClient_mockFile_handleClientConnected(const CEvent&, void* vlistener);
void sendToClient_mockFile_fileRecieveComplete(const CEvent& event, void*);
void sendToServer_mockData_handleClientConnected(const CEvent&, void* vlistener);
void sendToServer_mockData_fileRecieveComplete(const CEvent& event, void*);
void sendToServer_mockFile_handleClientConnected(const CEvent&, void* vlistener);
void sendToServer_mockFile_fileRecieveComplete(const CEvent& event, void*);
public:
CTestEventQueue m_events;
UInt8* m_mockData;
size_t m_mockDataSize;
fstream m_mockFile;
size_t m_mockFileSize;
};
TEST_F(NetworkTests, sendFileToClient)
TEST_F(NetworkTests, sendToClient_mockData)
{
// server and client
CNetworkAddress serverAddress(TEST_HOST, TEST_PORT);
@ -88,7 +123,7 @@ TEST_F(NetworkTests, sendFileToClient)
m_events.adoptHandler(
m_events.forCClientListener().connected(), &listener,
new TMethodEventJob<NetworkTests>(
this, &NetworkTests::sendFileToClient_handleClientConnected, &listener));
this, &NetworkTests::sendToClient_mockData_handleClientConnected, &listener));
ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true));
ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter));
@ -102,43 +137,196 @@ TEST_F(NetworkTests, sendFileToClient)
CSocketMultiplexer clientSocketMultiplexer;
CTCPSocketFactory* clientSocketFactory = new CTCPSocketFactory(&m_events, &clientSocketMultiplexer);
ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(sendFileToClient_getShape));
ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(sendFileToClient_getCursorPos));
ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape));
ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos));
CClient client(&m_events, "stub", serverAddress, clientSocketFactory, NULL, &clientScreen, cryptoOptions);
m_events.adoptHandler(
m_events.forIScreen().fileRecieveComplete(), &client,
new TMethodEventJob<NetworkTests>(
this, &NetworkTests::sendFileToClient_fileRecieveComplete));
this, &NetworkTests::sendToClient_mockData_fileRecieveComplete));
client.connect();
m_events.initQuitTimeout(10);
m_events.initQuitTimeout(5);
m_events.loop();
m_events.cleanupQuitTimeout();
}
TEST_F(NetworkTests, sendToClient_mockFile)
{
// server and client
CNetworkAddress serverAddress(TEST_HOST, TEST_PORT);
CCryptoOptions cryptoOptions;
serverAddress.resolve();
// server
CSocketMultiplexer serverSocketMultiplexer;
CTCPSocketFactory* serverSocketFactory = new CTCPSocketFactory(&m_events, &serverSocketMultiplexer);
CClientListener listener(serverAddress, serverSocketFactory, NULL, cryptoOptions, &m_events);
NiceMock<CMockScreen> serverScreen;
NiceMock<CMockPrimaryClient> primaryClient;
NiceMock<CMockConfig> serverConfig;
NiceMock<CMockInputFilter> serverInputFilter;
m_events.adoptHandler(
m_events.forCClientListener().connected(), &listener,
new TMethodEventJob<NetworkTests>(
this, &NetworkTests::sendToClient_mockFile_handleClientConnected, &listener));
ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true));
ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter));
CServer server(serverConfig, &primaryClient, &serverScreen, &m_events);
server.m_mock = true;
listener.setServer(&server);
// client
NiceMock<CMockScreen> clientScreen;
CSocketMultiplexer clientSocketMultiplexer;
CTCPSocketFactory* clientSocketFactory = new CTCPSocketFactory(&m_events, &clientSocketMultiplexer);
ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape));
ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos));
CClient client(&m_events, "stub", serverAddress, clientSocketFactory, NULL, &clientScreen, cryptoOptions);
m_events.adoptHandler(
m_events.forIScreen().fileRecieveComplete(), &client,
new TMethodEventJob<NetworkTests>(
this, &NetworkTests::sendToClient_mockFile_fileRecieveComplete));
client.connect();
m_events.initQuitTimeout(5);
m_events.loop();
m_events.cleanupQuitTimeout();
}
TEST_F(NetworkTests, sendToServer_mockData)
{
// server and client
CNetworkAddress serverAddress(TEST_HOST, TEST_PORT);
CCryptoOptions cryptoOptions;
serverAddress.resolve();
// server
CSocketMultiplexer serverSocketMultiplexer;
CTCPSocketFactory* serverSocketFactory = new CTCPSocketFactory(&m_events, &serverSocketMultiplexer);
CClientListener listener(serverAddress, serverSocketFactory, NULL, cryptoOptions, &m_events);
NiceMock<CMockScreen> serverScreen;
NiceMock<CMockPrimaryClient> primaryClient;
NiceMock<CMockConfig> serverConfig;
NiceMock<CMockInputFilter> serverInputFilter;
ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true));
ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter));
CServer server(serverConfig, &primaryClient, &serverScreen, &m_events);
server.m_mock = true;
listener.setServer(&server);
// client
NiceMock<CMockScreen> clientScreen;
CSocketMultiplexer clientSocketMultiplexer;
CTCPSocketFactory* clientSocketFactory = new CTCPSocketFactory(&m_events, &clientSocketMultiplexer);
ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape));
ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos));
CClient client(&m_events, "stub", serverAddress, clientSocketFactory, NULL, &clientScreen, cryptoOptions);
m_events.adoptHandler(
m_events.forCClientListener().connected(), &listener,
new TMethodEventJob<NetworkTests>(
this, &NetworkTests::sendToServer_mockData_handleClientConnected, &client));
m_events.adoptHandler(
m_events.forIScreen().fileRecieveComplete(), &server,
new TMethodEventJob<NetworkTests>(
this, &NetworkTests::sendToServer_mockData_fileRecieveComplete));
client.connect();
m_events.initQuitTimeout(5);
m_events.loop();
m_events.cleanupQuitTimeout();
}
TEST_F(NetworkTests, sendToServer_mockFile)
{
// server and client
CNetworkAddress serverAddress(TEST_HOST, TEST_PORT);
CCryptoOptions cryptoOptions;
serverAddress.resolve();
// server
CSocketMultiplexer serverSocketMultiplexer;
CTCPSocketFactory* serverSocketFactory = new CTCPSocketFactory(&m_events, &serverSocketMultiplexer);
CClientListener listener(serverAddress, serverSocketFactory, NULL, cryptoOptions, &m_events);
NiceMock<CMockScreen> serverScreen;
NiceMock<CMockPrimaryClient> primaryClient;
NiceMock<CMockConfig> serverConfig;
NiceMock<CMockInputFilter> serverInputFilter;
ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true));
ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter));
CServer server(serverConfig, &primaryClient, &serverScreen, &m_events);
server.m_mock = true;
listener.setServer(&server);
// client
NiceMock<CMockScreen> clientScreen;
CSocketMultiplexer clientSocketMultiplexer;
CTCPSocketFactory* clientSocketFactory = new CTCPSocketFactory(&m_events, &clientSocketMultiplexer);
ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape));
ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos));
CClient client(&m_events, "stub", serverAddress, clientSocketFactory, NULL, &clientScreen, cryptoOptions);
m_events.adoptHandler(
m_events.forCClientListener().connected(), &listener,
new TMethodEventJob<NetworkTests>(
this, &NetworkTests::sendToServer_mockFile_handleClientConnected, &client));
m_events.adoptHandler(
m_events.forIScreen().fileRecieveComplete(), &server,
new TMethodEventJob<NetworkTests>(
this, &NetworkTests::sendToServer_mockFile_fileRecieveComplete));
client.connect();
m_events.initQuitTimeout(5);
m_events.loop();
m_events.cleanupQuitTimeout();
}
void
NetworkTests::sendFileToClient_handleClientConnected(const CEvent&, void* vlistener)
NetworkTests::sendToClient_mockData_handleClientConnected(const CEvent&, void* vlistener)
{
CClientListener* listener = reinterpret_cast<CClientListener*>(vlistener);
CServer* server = listener->getServer();
CClientProxy* client = listener->getNextClient();
if (client == NULL) {
throw std::exception("client is null");
throw runtime_error("client is null");
}
CBaseClientProxy* bcp = reinterpret_cast<CBaseClientProxy*>(client);
server->adoptClient(bcp);
server->setActive(bcp);
sendData(server);
sendMockData(server);
}
void
NetworkTests::sendFileToClient_fileRecieveComplete(const CEvent& event, void*)
NetworkTests::sendToClient_mockData_fileRecieveComplete(const CEvent& event, void*)
{
CClient* client = reinterpret_cast<CClient*>(event.getTarget());
EXPECT_TRUE(client->isReceivedFileSizeValid());
@ -147,32 +335,166 @@ NetworkTests::sendFileToClient_fileRecieveComplete(const CEvent& event, void*)
}
void
NetworkTests::sendData(CServer* server)
NetworkTests::sendToClient_mockFile_handleClientConnected(const CEvent&, void* vlistener)
{
UInt8* largeDataSize = new UInt8[5];
largeDataSize[0] = '0';
largeDataSize[1] = '5';
largeDataSize[2] = '1';
largeDataSize[3] = '1';
largeDataSize[4] = '\0';
CClientListener* listener = reinterpret_cast<CClientListener*>(vlistener);
CServer* server = listener->getServer();
// transfer data from server -> client
m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), server, largeDataSize));
CClientProxy* client = listener->getNextClient();
if (client == NULL) {
throw runtime_error("client is null");
}
UInt8* largeData = new UInt8[klargeDataSize + 1];
largeData[0] = '1';
memcpy(&largeData[1], g_largeData, klargeDataSize);
m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), server, (UInt8*)largeData));
CBaseClientProxy* bcp = reinterpret_cast<CBaseClientProxy*>(client);
server->adoptClient(bcp);
server->setActive(bcp);
UInt8* transferFinished = new UInt8[2];
transferFinished[0] = '2';
transferFinished[1] = '\0';
m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), server, transferFinished));
server->sendFileToClient(kMockFilename);
}
void
sendFileToClient_getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h)
NetworkTests::sendToClient_mockFile_fileRecieveComplete(const CEvent& event, void*)
{
CClient* client = reinterpret_cast<CClient*>(event.getTarget());
EXPECT_TRUE(client->isReceivedFileSizeValid());
m_events.raiseQuitEvent();
}
void
NetworkTests::sendToServer_mockData_handleClientConnected(const CEvent&, void* vclient)
{
CClient* client = reinterpret_cast<CClient*>(vclient);
sendMockData(client);
}
void
NetworkTests::sendToServer_mockData_fileRecieveComplete(const CEvent& event, void*)
{
CServer* server = reinterpret_cast<CServer*>(event.getTarget());
EXPECT_TRUE(server->isReceivedFileSizeValid());
m_events.raiseQuitEvent();
}
void
NetworkTests::sendToServer_mockFile_handleClientConnected(const CEvent&, void* vclient)
{
CClient* client = reinterpret_cast<CClient*>(vclient);
client->sendFileToServer(kMockFilename);
}
void
NetworkTests::sendToServer_mockFile_fileRecieveComplete(const CEvent& event, void*)
{
CServer* server = reinterpret_cast<CServer*>(event.getTarget());
EXPECT_TRUE(server->isReceivedFileSizeValid());
m_events.raiseQuitEvent();
}
void
NetworkTests::sendMockData(void* eventTarget)
{
// send first message (file size)
CString size = intToString(kMockDataSize);
UInt32 sizeLength = size.size();
CFileChunker::CFileChunk* sizeMessage = new CFileChunker::CFileChunk(sizeLength + 2);
char* chunkData = sizeMessage->m_chunk;
chunkData[0] = kFileStart;
memcpy(&chunkData[1], size.c_str(), sizeLength);
chunkData[sizeLength + 1] = '\0';
m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), eventTarget, sizeMessage));
// send chunk messages with incrementing chunk size
size_t lastSize = 0;
size_t sentLength = 0;
while (true) {
size_t chunkSize = lastSize + kMockDataChunkIncrement;
// make sure we don't read too much from the mock data.
if (sentLength + chunkSize > kMockDataSize) {
chunkSize = kMockDataSize - sentLength;
}
// first byte is the chunk mark, last is \0
CFileChunker::CFileChunk* fileChunk = new CFileChunker::CFileChunk(chunkSize + 2);
char* chunkData = fileChunk->m_chunk;
chunkData[0] = kFileChunk;
memcpy(&chunkData[1], &m_mockData[sentLength], chunkSize);
chunkData[chunkSize + 1] = '\0';
m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), eventTarget, fileChunk));
sentLength += chunkSize;
lastSize = chunkSize;
if (sentLength == kMockDataSize) {
break;
}
}
// send last message
CFileChunker::CFileChunk* transferFinished = new CFileChunker::CFileChunk(2);
chunkData = transferFinished->m_chunk;
chunkData[0] = kFileEnd;
chunkData[1] = '\0';
m_events.addEvent(CEvent(m_events.forIScreen().fileChunkSending(), eventTarget, transferFinished));
}
UInt8*
newMockData(size_t size)
{
UInt8* buffer = new UInt8[size];
UInt8* data = buffer;
const UInt8 head[] = "mock head... ";
size_t headSize = sizeof(head) - 1;
const UInt8 tail[] = "... mock tail";
size_t tailSize = sizeof(tail) - 1;
const UInt8 synergyRocks[] = "synergy\0 rocks! ";
size_t synergyRocksSize = sizeof(synergyRocks) - 1;
memcpy(data, head, headSize);
data += headSize;
SInt32 times = (size - headSize - tailSize) / synergyRocksSize;
for (SInt32 i = 0; i < times; ++i) {
memcpy(data, synergyRocks, synergyRocksSize);
data += synergyRocksSize;
}
SInt32 remainder = (size - headSize - tailSize) % synergyRocksSize;
if (remainder != 0) {
memset(data, '.', remainder);
data += remainder;
}
memcpy(data, tail, tailSize);
return buffer;
}
void
createFile(fstream& file, const char* filename, size_t size)
{
UInt8* buffer = newMockData(size);
file.open(filename, ios::out | ios::binary);
if (!file.is_open()) {
throw runtime_error("file not open");
}
file.write(reinterpret_cast<char*>(buffer), size);
file.close();
delete[] buffer;
}
void
getScreenShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h)
{
x = 0;
y = 0;
@ -181,8 +503,16 @@ sendFileToClient_getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h)
}
void
sendFileToClient_getCursorPos(SInt32& x, SInt32& y)
getCursorPos(SInt32& x, SInt32& y)
{
x = 0;
y = 0;
}
CString
intToString(size_t i)
{
stringstream ss;
ss << i;
return ss.str();
}

View File

@ -20,8 +20,8 @@
#include <gmock/gmock.h>
#include "COSXKeyState.h"
#include "CMockKeyMap.h"
#include "CMockEventQueue.h"
#include "synergy/CMockKeyMap.h"
#include "synergy/CMockEventQueue.h"
#include "CLog.h"

View File

@ -22,8 +22,8 @@
#define TEST_ENV
#include "Global.h"
#include "CMockKeyMap.h"
#include "CMockEventQueue.h"
#include "synergy/CMockKeyMap.h"
#include "synergy/CMockEventQueue.h"
#include "CXWindowsKeyState.h"
#include "CLog.h"
#include <errno.h>

View File

@ -18,7 +18,7 @@
#include <gtest/gtest.h>
#include "CXWindowsScreen.h"
#include "CMockEventQueue.h"
#include "synergy/CMockEventQueue.h"
using ::testing::_;

View File

@ -48,8 +48,8 @@ TEST(CServerProxyTests, mouseMove)
g_mouseMove_bufferIndex = 0;
NiceMock<CMockEventQueue> eventQueue;
NiceMock<CMockClient> client;
NiceMock<CMockStream> stream;
NiceMock<CMockClient> client;
IStreamEvents streamEvents;
streamEvents.setEvents(&eventQueue);