diff --git a/src/qproxyhandler.cpp b/src/qproxyhandler.cpp index 453347e..9e2fc36 100644 --- a/src/qproxyhandler.cpp +++ b/src/qproxyhandler.cpp @@ -46,6 +46,7 @@ QByteArray QProxyHandlerPrivate::methodToString(QHttpSocket::Method method) case QHttpSocket::DELETE: return "DELETE"; case QHttpSocket::TRACE: return "TRACE"; case QHttpSocket::CONNECT: return "CONNECT"; + default: return QByteArray(); } } @@ -61,7 +62,7 @@ void QProxyHandler::process(QHttpSocket *socket, const QString &path) socket->setParent(this); // Build the request for the upstream server - QUrl url(QString("http://%s:%d/%s").arg(d->address.toString()).arg(d->port).arg(path)); + QUrl url(QString("http://%1:%2/%3").arg(d->address.toString()).arg(d->port).arg(path)); QNetworkRequest request(url); // Copy all of the request headers @@ -72,7 +73,8 @@ void QProxyHandler::process(QHttpSocket *socket, const QString &path) // Begin the request QNetworkReply *reply = d->networkAccessManager.sendCustomRequest( request, - d->methodToString(socket->method()) + d->methodToString(socket->method()), + socket ); reply->setParent(this); @@ -89,16 +91,8 @@ void QProxyHandler::process(QHttpSocket *socket, const QString &path) socket->write(reply->readAll()); }); - // When data arrives from downstream, send it upstream - connect(socket, &QHttpSocket::readyRead, [socket, reply]() { - reply->write(socket->readAll()); - }); - // When either end disconnects, sever everything and tear it down - auto teardown = [socket, reply]() { - socket->deleteLater(); - reply->deleteLater(); - }; - connect(reply, &QNetworkReply::finished, teardown); - connect(socket, &QHttpSocket::aboutToClose, teardown); + connect(socket, &QHttpSocket::aboutToClose, reply, &QNetworkReply::close); + connect(reply, &QNetworkReply::finished, socket, &QHttpSocket::close); + connect(reply, &QNetworkReply::finished, reply, &QNetworkReply::deleteLater); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 521e77b..b2f343d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,7 @@ set(TESTS TestQLocalAuth TestQLocalFile TestQObjectHandler + TestQProxyHandler ) qt5_add_resources(QRC resource.qrc) diff --git a/tests/TestQProxyHandler.cpp b/tests/TestQProxyHandler.cpp new file mode 100644 index 0000000..0554e04 --- /dev/null +++ b/tests/TestQProxyHandler.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015 Nathan Osman + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "common/qsimplehttpclient.h" +#include "common/qsocketpair.h" + +const QString Path = "test"; +const QByteArray Data = "test"; + +class TestQProxyHandler : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + + void testDataPassthrough(); +}; + +void TestQProxyHandler::testDataPassthrough() +{ + // Create the upstream handler (simple echo) + QObjectHandler upstreamHandler; + upstreamHandler.registerMethod(Path, [](QHttpSocket *socket) { + socket->write(socket->readAll()); + socket->close(); + }, true); + + // Create the upstream server and begin listening + QHttpServer upstreamServer(&upstreamHandler); + QVERIFY(upstreamServer.listen(QHostAddress::LocalHost)); + + // Create the proxy handler + QProxyHandler handler(upstreamServer.serverAddress(), upstreamServer.serverPort()); + + QSocketPair pair; + QTRY_VERIFY(pair.isConnected()); + + QSimpleHttpClient client(pair.client()); + QHttpSocket socket(pair.server(), &pair); + + // Send the headers and wait for them to be parsed + QHttpSocket::HeaderMap headers{ + {"Content-Length", QByteArray::number(Data.length())} + }; + client.sendHeaders("POST", QString("/%1").arg(Path).toUtf8(), headers); + QTRY_VERIFY(socket.isHeadersParsed()); + + // Route the request (triggering the upstream connection) + handler.route(&socket, Path); + + // Send the data and wait for it to return + client.sendData(Data); + QTRY_COMPARE(client.data(), Data); +} + +QTEST_MAIN(TestQProxyHandler) +#include "TestQProxyHandler.moc"