diff --git a/include/QHttpEngine/QHttpMiddleware b/include/QHttpEngine/QHttpMiddleware new file mode 100644 index 0000000..f857899 --- /dev/null +++ b/include/QHttpEngine/QHttpMiddleware @@ -0,0 +1 @@ +#include "qhttpmiddleware.h" diff --git a/include/QHttpEngine/qhttphandler.h b/include/QHttpEngine/qhttphandler.h index 280d2e3..de69a1d 100644 --- a/include/QHttpEngine/qhttphandler.h +++ b/include/QHttpEngine/qhttphandler.h @@ -28,6 +28,7 @@ #include "qhttpengine_global.h" class QRegExp; +class QHttpMiddleware; class QHttpSocket; class QHTTPENGINE_EXPORT QHttpHandlerPrivate; @@ -79,6 +80,11 @@ public: */ explicit QHttpHandler(QObject *parent = 0); + /** + * @brief Add middleware to the handler + */ + void addMiddleware(QHttpMiddleware *middleware); + /** * @brief Add a redirect for a specific pattern * diff --git a/include/QHttpEngine/qhttpmiddleware.h b/include/QHttpEngine/qhttpmiddleware.h new file mode 100644 index 0000000..88e8973 --- /dev/null +++ b/include/QHttpEngine/qhttpmiddleware.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef QHTTPENGINE_QHTTPMIDDLEWARE_H +#define QHTTPENGINE_QHTTPMIDDLEWARE_H + +#include + +#include "qhttpengine_global.h" + +class QHttpSocket; + +/** + * @brief Pre-handler request processor + * + * Middleware sits between the server and the final request handler, + * determining whether the request should be passed on to the handler. + */ +class QHTTPENGINE_EXPORT QHttpMiddleware : public QObject +{ + Q_OBJECT + +public: + + /** + * @brief Base constructor for middleware + */ + explicit QHttpMiddleware(QObject *parent = Q_NULLPTR) : QObject(parent) {} + + /** + * @brief Determine if request processing should continue + * + * This method is invoked when a new request comes in. If true is + * returned, processing continues. Otherwise, it is assumed that an + * appropriate error was written to the socket. + */ + virtual bool process(QHttpSocket *socket) = 0; +}; + +#endif // QHTTPENGINE_QHTTPMIDDLEWARE_H diff --git a/src/qhttphandler.cpp b/src/qhttphandler.cpp index 571ca06..0490c17 100644 --- a/src/qhttphandler.cpp +++ b/src/qhttphandler.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include "qhttphandler_p.h" @@ -37,6 +38,11 @@ QHttpHandler::QHttpHandler(QObject *parent) { } +void QHttpHandler::addMiddleware(QHttpMiddleware *middleware) +{ + d->middleware.append(middleware); +} + void QHttpHandler::addRedirect(const QRegExp &pattern, const QString &path) { d->redirects.append(Redirect(pattern, path)); @@ -49,6 +55,13 @@ void QHttpHandler::addSubHandler(const QRegExp &pattern, QHttpHandler *handler) void QHttpHandler::route(QHttpSocket *socket, const QString &path) { + // Run through each of the middleware + foreach (QHttpMiddleware *middleware, d->middleware) { + if (!middleware->process(socket)) { + return; + } + } + // Check each of the redirects for a match foreach (Redirect redirect, d->redirects) { if (redirect.first.indexIn(path) != -1) { diff --git a/src/qhttphandler_p.h b/src/qhttphandler_p.h index bd0592a..84744eb 100644 --- a/src/qhttphandler_p.h +++ b/src/qhttphandler_p.h @@ -43,6 +43,7 @@ public: QList redirects; QList subHandlers; + QList middleware; private: diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2003192..27dc45d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(common) set(TESTS TestQFilesystemHandler TestQHttpHandler + TestQHttpMiddleware TestQHttpParser TestQHttpRange TestQHttpServer diff --git a/tests/TestQHttpMiddleware.cpp b/tests/TestQHttpMiddleware.cpp new file mode 100644 index 0000000..de9eaae --- /dev/null +++ b/tests/TestQHttpMiddleware.cpp @@ -0,0 +1,74 @@ +/* + * 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 "common/qsimplehttpclient.h" +#include "common/qsocketpair.h" + +class DummyMiddleware : public QHttpMiddleware +{ + Q_OBJECT + +public: + + virtual bool process(QHttpSocket *socket) + { + socket->writeError(QHttpSocket::Forbidden); + return false; + } +}; + +class TestQHttpMiddleware : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + + void testProcess(); +}; + +void TestQHttpMiddleware::testProcess() +{ + QSocketPair pair; + QTRY_VERIFY(pair.isConnected()); + + QSimpleHttpClient client(pair.client()); + QHttpSocket socket(pair.server(), &pair); + + client.sendHeaders("GET", "/"); + QTRY_VERIFY(socket.isHeadersParsed()); + + DummyMiddleware middleware; + QHttpHandler handler; + handler.addMiddleware(&middleware); + handler.route(&socket, "/"); + + QTRY_COMPARE(client.statusCode(), static_cast(QHttpSocket::Forbidden)); +} + +QTEST_MAIN(TestQHttpMiddleware) +#include "TestQHttpMiddleware.moc"