Skip to content

Commit

Permalink
Merge pull request #156 from openstreetmap/issue-147-remote-control-d…
Browse files Browse the repository at this point in the history
…ownloads-multiple-times

Moved remote control to a separate class, fixing a few bugs.
  • Loading branch information
Krakonos authored Sep 22, 2019
2 parents 4389ed5 + db52b6e commit 4903187
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 48 deletions.
63 changes: 17 additions & 46 deletions src/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "Utils/Utils.h"
#include "DirtyList.h"
#include "DirtyListExecutorOSC.h"
#include "RemoteControlServer.hpp"

#include <ui_MainWindow.h>
#include <ui_AboutDialog.h>
Expand Down Expand Up @@ -119,6 +120,8 @@ const QString MIME_MERKAARTOR_UNDO_XML = "application/x-merkaartor-undo+xml";

} // namespace

using namespace Merkaartor;

class MainWindowPrivate
{
public:
Expand All @@ -144,7 +147,7 @@ class MainWindowPrivate
FeaturesDock* theFeats;
QString title;
QActionGroup* projActgrp;
QTcpServer* theListeningServer;
RemoteControlServer* theListeningServer;
PropertiesDock* theProperties;
RendererOptions renderOptions;
int latSaveDirtyLevel;
Expand Down Expand Up @@ -472,11 +475,12 @@ void MainWindow::delayedInit()

updateWindowMenu();

p->theListeningServer = new RemoteControlServer(this);
connect( p->theListeningServer, &RemoteControlServer::requestReceived,
this, [this](QString url) { loadUrl(url); } );

if (M_PREFS->getLocalServer()) {
p->theListeningServer = new QTcpServer(this);
connect(p->theListeningServer, SIGNAL(newConnection()), this, SLOT(incomingLocalConnection()));
if (!p->theListeningServer->listen(QHostAddress::LocalHost, 8111))
qDebug() << "Remote control: Unable to listen on 8111";
p->theListeningServer->listen();
}

if (M_PREFS->getHideToolbarLabels()) {
Expand Down Expand Up @@ -1065,37 +1069,6 @@ bool MainWindow::eventFilter(QObject */* watched */, QEvent *event)
return false;
}

void MainWindow::incomingLocalConnection()
{
QTcpSocket *clientConnection = p->theListeningServer->nextPendingConnection();
connect(clientConnection, SIGNAL(disconnected()),
clientConnection, SLOT(deleteLater()));
connect(clientConnection, SIGNAL(readyRead()), this, SLOT(readLocalConnection()) );
}

void MainWindow::readLocalConnection()
{
QTcpSocket* socket = (QTcpSocket*)sender();
if ( socket->canReadLine() ) {
QString ln = socket->readLine();
QStringList tokens = ln.split( QRegExp("[ \r\n][ \r\n]*"), QString::SkipEmptyParts );
if ( tokens[0] == "GET" ) {
QTextStream resultStream(socket);
resultStream << "HTTP/1.1 200 OK\r\n";
resultStream << "Date: " << QDateTime::currentDateTime().toString(Qt::TextDate);
resultStream << "Server: Merkaartor RemoteControl\r\n";
resultStream << "Content-type: text/plain\r\n";
resultStream << "Access-Control-Allow-Origin: *\r\n";
resultStream << "Content-length: 4\r\n\r\n";
resultStream << "OK\r\n";
socket->disconnectFromHost();

QUrl u = QUrl(tokens[1]);
loadUrl(u);
}
}
}

namespace {

void AddActionsIntoManager(QtToolBarManager* manager, QWidget* widget,
Expand Down Expand Up @@ -1846,6 +1819,12 @@ bool MainWindow::importFiles(Document * mapDocument, const QStringList & fileNam
return foundImport;
}

void MainWindow::loadUrl(const QString& urlString)
{
qDebug() << "Loading url: " << urlString;
loadUrl(QUrl(urlString));
}

void MainWindow::loadUrl(const QUrl& theUrl)
{
activateWindow();
Expand Down Expand Up @@ -3108,17 +3087,9 @@ void MainWindow::preferencesChanged(PreferencesDialog* prefs)
}
}
if (M_PREFS->getLocalServer()) {
if (!p->theListeningServer) {
p->theListeningServer = new QTcpServer(this);
connect(p->theListeningServer, SIGNAL(newConnection()), this, SLOT(incomingLocalConnection()));
if (!p->theListeningServer->listen(QHostAddress::LocalHost, 8111))
qDebug() << "Remote control: Unable to listen on 8111";
}
p->theListeningServer->listen();
} else {
if (p->theListeningServer) {
delete p->theListeningServer;
p->theListeningServer = NULL;
}
p->theListeningServer->close();
}

applyStyles(prefs->cbStyles->itemData(prefs->cbStyles->currentIndex()).toString());
Expand Down
3 changes: 1 addition & 2 deletions src/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ public slots:

public:
bool importFiles(Document * mapDocument, const QStringList & filesNames, QStringList * importedFileNames = NULL, bool useGdal = false);
void loadUrl(const QString& urlString);
void loadUrl(const QUrl& u);
void loadDocument(QString fn);
void loadTemplateDocument(QString fn);
Expand Down Expand Up @@ -303,8 +304,6 @@ private slots:
void setAreaOpacity(QAction*);
void updateBookmarksMenu();
void updateWindowMenu(bool b=false);
void incomingLocalConnection();
void readLocalConnection();

void on_viewWireframeAction_toggled(bool arg1);

Expand Down
91 changes: 91 additions & 0 deletions src/Utils/RemoteControlServer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/******************************************************************************
* Filename: RemoteControlServer.cpp
*
* Created: 2018/07/29 22:20
* Author: Ladislav Láska
* e-mail: krakonos@krakonos.org
*
******************************************************************************/

#include <QTcpSocket>
#include <QDateTime>

#include "RemoteControlServer.hpp"

using namespace Merkaartor;
using namespace Merkaartor::RemoteControlServerPriv;

void RemoteControlConnection::readyRead() {
qDebug() << "RemoteControlConnection: readyRead.";
if ( m_socket->canReadLine() ) {
qDebug() << "RemoteControlConnection: canReadLine.";
QString ln = m_socket->readLine();
QStringList tokens = ln.split( QRegExp("[ \r\n][ \r\n]*"), QString::SkipEmptyParts );
if ( tokens[0] == "GET" ) {
m_responseStream << "HTTP/1.1 200 OK\r\n";
m_responseStream << "Date: " << QDateTime::currentDateTime().toString(Qt::TextDate);
m_responseStream << "Server: Merkaartor RemoteControl\r\n";
m_responseStream << "Content-type: text/plain\r\n";
m_responseStream << "Access-Control-Allow-Origin: *\r\n";
m_responseStream << "Content-length: 4\r\n\r\n";
m_responseStream << "OK\r\n";
m_responseStream << flush;
m_socket->disconnectFromHost();

qDebug() << "RemoteControlConnection: url read, response sent.";
emit requestReceived(tokens[1]);
}
}
}

RemoteControlConnection::RemoteControlConnection( QTcpSocket *socket )
: m_socket(socket), m_responseStream(socket)
{
connect( m_socket, &QTcpSocket::readyRead, this, &RemoteControlConnection::readyRead);
connect( m_socket, &QTcpSocket::disconnected, m_socket, &QTcpSocket::deleteLater );
connect( m_socket, &QTcpSocket::destroyed, this, &QObject::deleteLater );
}


RemoteControlServer::RemoteControlServer( QObject* parent )
:QObject(parent)
{
m_tcpServer = new QTcpServer(this);
connect(m_tcpServer, &QTcpServer::newConnection, this, &RemoteControlServer::newConnection );
}

void RemoteControlServer::newConnection() {
QTcpSocket *socket = m_tcpServer->nextPendingConnection();
if (socket == nullptr) {
qDebug() << "RemoteControlServer::newConnection invoked, but no connection is pending.";
return;
}
/* The RemoteControlconnection will handle it's own destruction when the connection is broken. */
auto connHandler = new RemoteControlConnection(socket);
/* Note:
* Qt::QueuedConnection is a workaround for a problem hit in issue #147.
* When this signal is called, it triggers a long sequence of events, some
* of them opening it's own QEventLoop (dialogs). This seems to cause
* issues in deleting the RemoteControlConnection objects, specifically the
* QTcpSocket inside is destroyed, and after that a signal is called on it.
* This seems to be a bug in Qt, as we correctly use deleteLater().
* However, there might be more to it, as I have been unable to put
* together a minimal testcase.
*
* For now, we make sure the main EventLoop is executing the download
* dialogs and hopefully avoid this problem.
*/
connect( connHandler, &RemoteControlConnection::requestReceived,
this, [this](QString requestUrl) { emit requestReceived(requestUrl); }, Qt::QueuedConnection);

}

void RemoteControlServer::listen() {
if (!m_tcpServer->listen( QHostAddress::LocalHost, 8111 )) {
qWarning() << "Unable to open port localhost:8111: " << m_tcpServer->errorString();
}
}

void RemoteControlServer::close() {
m_tcpServer->close();
}
77 changes: 77 additions & 0 deletions src/Utils/RemoteControlServer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/******************************************************************************
* Filename: RemoteControlServer.hpp
*
* Created: 2018/07/29 22:08
* Author: Ladislav Láska
* e-mail: krakonos@krakonos.org
*
******************************************************************************/

#ifndef __REMOTECONTROLSERVER_HPP__
#define __REMOTECONTROLSERVER_HPP__

#include <QObject>
#include <QTcpServer>


namespace Merkaartor {
namespace RemoteControlServerPriv {

/**
* Instance is spawned for each connection to the remote control port. Once a
* request is received, it emits requestReceived signal and closes the
* connection gracefully.
*
* This object shall not be used outside of RemoteControlServer. The server
* proxies the requestReceived() signal.
*
* The object destroys itself after the connection is closed.
*/
class RemoteControlConnection : public QObject {
Q_OBJECT
public:
RemoteControlConnection( QTcpSocket *socket );
public slots:
void readyRead();
signals:
void requestReceived(QString requestUrl);
private:
QTcpSocket* m_socket;
QTextStream m_responseStream;
};

}

class RemoteControlServer : public QObject {
Q_OBJECT

public:
RemoteControlServer(QObject* parent = nullptr);

/** Start listening for remote requests. Request is automatically
* accepted and appropriate requestReceived signal is emitted. This
* includes invalid requests as well. */
void listen();

/**
* Close the port and stop listening. No further requests will be
* received, but pending connections might still trigger events. */
void close();

signals:
/**
* Emitted every time a new remote control request is received.
*/
void requestReceived(QString requestUrl);

private slots:
/** Internal slot to handle incoming TCP connections. */
void newConnection();

private:
QTcpServer* m_tcpServer;
};

} /* namespace Merkaartor */

#endif
2 changes: 2 additions & 0 deletions src/Utils/Utils.pri
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ HEADERS += \
EditCompleterDelegate.h \
PictureViewerDialog.h \
PixmapWidget.h \
RemoteControlServer.hpp \
SelectionDialog.h \
SvgCache.h \
MDiscardableDialog.h \
Expand All @@ -23,6 +24,7 @@ SOURCES += \
EditCompleterDelegate.cpp \
PictureViewerDialog.cpp \
PixmapWidget.cpp \
RemoteControlServer.cpp \
SelectionDialog.cpp \
SvgCache.cpp \
MDiscardableDialog.cpp \
Expand Down

0 comments on commit 4903187

Please sign in to comment.