diff --git a/third_party/QtWebApp/CHANGELOG.txt b/third_party/QtWebApp/CHANGELOG.txt new file mode 100644 index 00000000..e24ec4af --- /dev/null +++ b/third_party/QtWebApp/CHANGELOG.txt @@ -0,0 +1,290 @@ +Dont forget to update the release number also in +QtWebApp.pro and httpserver/httpglobal.cpp. + +1.7.11 +28.12.2019 +Fix Http Headers are not properly received if the two characters of +a line-break (\r\n) were not received together in the same ethernet +package. + +1.7.10 +04.12.2019 +Add support for other SSL implementations than OpenSSL (as far Qt supports it). +Fix log bufffer was triggered only by severities above minLevel (should be "at least" minLevel). + +1.7.9 +20.06.2019 +INFO messages do not trigger writing out buffered log messages anymore when +bufferSize>0 and minLevel>0. + +1.7.8 +05.02.2019 +HttpConnectionHandler closes the socket now in the thread of the socket. +Headers and Body sent to the browser are now separated into individual ethernet packets. + +1.7.7 +04.02.2019 +HttpConnectionHandler creates a new Qthread instead of being itself a QThread. +Improved formatting of thread ID in logger. + +1.7.6 +18.01.2019 +Code cleanup with const keywords and type conversions. +Update Documentation. + +1.7.5 +17.01.2019 +Added content-types for *.xml and *.json to the StaticFileController. +Fixed locking and memory leak in HttpSession. + +1.7.4 +24.05.2018 +Fixed two possible null-pointer references in case of broken HTTP requests. + +1.7.3 +25.04.2017 +Wait until all data are sent before closing connections. + +1.7.2 +17.01.2017 +Fixed compile error with MSVC. + +1.7.1 +10.11.2016 +Fixed a possible memory leak in case of broken Multipart HTTP Requests. + +1.7.0 +08.11.2016 +Introduced namespace "stefanfrings". +Improved performance a little. + +1.6.7 +10.10.2016 +Fix type of socketDescriptor in qtservice library. +Add support for INFO log messages (new since QT 5.5). +Improve indentation of log messages. + +1.6.6 +25.07.2016 +Removed useless mutex from TemplateLoader. +Add mutex to TemplateCache (which is now needed). + +1.6.5 +10.06.2016 +Incoming HTTP request headers are now processed case-insensitive. +Add support for the HttpOnly flag of cookies. + +1.6.4 +27.03.2016 +Fixed constructor of Template class did not load the source file properly. +Template loader and cache were not affected. + +1.6.3 +11.03.2016 +Fixed compilation error. +Added missing implementation of HttpRequest::getPeerAddress(). + +1.6.2 +06.03.2016 +Added mime types for some file extensions. + +1.6.1 +25.01.2016 +Fixed parser of boundary value in multi-part request, which caused that +QHttpMultipart did not work on client side. + +1.6.0 +29.12.2015 +Much better output buffering, reduces the number of small IP packages. + +1.5.13 +29.12.2015 +Improved performance a little. +Add support for old HTTP 1.0 clients. +Add HttpResposne::flush() and HttpResponse::isConnected() which are helpful to support +SSE from HTML 5 specification. + +1.5.12 +11.12.2015 +Fix program crash when using SSL with a variable sized thread pool on Windows. +Changed name of HttpSessionStore::timerEvent() to fix compiler warnings since Qt 5.0. +Add HttpRequest::getRawPath(). +HttpSessionStore::sessions is now protected. + +1.5.11 +21.11.2015 +Fix project file for Mac OS. +Add HttpRequest::getPeerAddress() and HttpResponse::getStatusCode(). + +1.5.10 +01.09.2015 +Modified StaticFileController to support ressource files (path starting with ":/" or "qrc://"). + +1.5.9 +06.08.2015 +New HttpListener::listen() method, to restart listening after close. +Add missing include for QObject in logger.h. +Add a call to flush() before closing connections, which solves an issue with nginx. + +1.5.8 +26.07.2015 +Fixed segmentation fault error when closing the application while a HTTP request is in progress. +New HttpListener::close() method to simplifly proper shutdown. + +1.5.7 +20.07.2015 +Fix Qt 5.5 compatibility issue. + +1.5.6 +22.06.2015 +Fixed compilation failes if QT does not support SSL. + +1.5.5 +16.06.2015 +Improved performance of SSL connections. + +1.5.4 +15.06.2015 +Support for Qt versions without OpenSsl. + +1.5.3 +22.05.2015 +Fixed Windows issue: QsslSocket cannot be closed from other threads than it was created in. + +1.5.2 +12.05.2015 +Fixed Windows issue: QSslSocket cannot send signals to another thread than it was created in. + +1.5.1 +14.04.2015 +Add support for pipelining. + +1.5.0 +03.04.2015 +Add support for HTTPS. + +1.4.2 +03.04.2015 +Fixed HTTP request did not work if it was split into multipe IP packages. + +1.4.1 +20.03.2015 +Fixed session cookie expires while the user is active, expiration time was not prolonged on each request. + +1.4.0 +14.03.2015 +This release has a new directory structure and new project files to support the creation of a shared library (*.dll or *.so). + +1.3.8 +12.03.2015 +Improved shutdown procedure. +New config setting "host" which binds the listener to a specific network interface. + +1.3.7 +14.01.2015 +Fixed setting maxMultiPartSize worked only with file-uploads but not with form-data. + +1.3.6 +16.09.2014 +Fixed DualFileLogger produces no output. + +1.3.5 +11.06.2014 +Fixed a multi-threading issue with race condition in StaticFileController. + +1.3.4 +04.06.2014 +Fixed wrong content type when the StaticFileController returns a cached index.html. + +1.3.3 +17.03.2014 +Improved security of StaticFileController by denying "/.." in any position of the request path. +Improved performance of StaticFileController a little. +New convenience method HttpResponse::redirect(url). +Fixed a missing return statement in StaticFileController. + +1.3.2 +08.01.2014 +Fixed HTTP Server ignoring URL parameters when the request contains POST parameters. + +1.3.1 +15.08.2013 +Fixed HTTP server not accepting connections on 64bit OS with QT 5. + +1.3.0 +20.04.2013 +Updated for compatibility QT 5. You may still use QT 4.7 or 4.8, if you like. +Also added support for logging source file name, line number and function name. + +1.2.13 +03.03.2013 +Fixed Logger writing wrong timestamp for buffered messages. +Improved shutdown procedure. The webserver now processes all final signals before the destructor finishes. + +1.2.12 +01.03.2013 +Fixed HttpResponse sending first part of data repeatedly when the amount of data is larger than the available memory for I/O buffer. + +1.2.11 +06.01.2013 +Added clearing the write buffer when accepting a new connection, so that it does not send remaining data from an aborted previous connection (which is possibly a bug in QT). + +1.2.10 +18.12.2012 +Reduced memory usage of HttpResponse in case of large response. + +1.2.9 +29.07.2012 +Added a mutex to HttpConnectionHandlerPool to fix a concurrency issue when a pooled object gets taken from the cache while it times out. +Modified HttpConnectionHandler so that it does not throw an exception anymore when a connection gets closed by the peer in the middle of a read. + +1.2.8 +22.07.2012 +Fixed a possible concurrency issue when the file cache is so small that it stores less files than the number of threads. + +1.2.7 +18.07.2012 +Fixed HttpRequest ignores additional URL parameters of POST requests. +Fixed HttpRequest ignores POST parameters of body if there is no Content-Type header. +Removed unused tempdir variable from HttpRequest. +Added mutex to cache of StaticFileController to prevent concurrency problems. +Removed HTTP response with status 408 after read timeout. Connection gets simply closed now. + +1.2.6 +29.06.2012 +Fixed a compilation error on 64 bit if super verbose debugging is enabled. +Fixed a typo in static file controller related to the document type header. + +1.2.5 +27.06.2012 +Fixed error message "QThread: Destroyed while thread is still running" during program termination. + +1.2.4 +02.06.2012 +Fixed template engine skipping variable tokens when a value is shorter than the token. + +1.2.3 +26.12.2011 +Fixed null pointer error when the HTTP server aborts a request that is too large. + +1.2.2 +06.11.2011 +Fixed compilation error on 64 bit platforms. + +1.2.1 +22.10.2011 +Fixed a multi-threading bug in HttpConnectionHandler. + +1.2.0 +05.12.2010 +Added a controller that serves static files, with cacheing. + + +1.1.0 +19.10.2010 +Added support for sessions. +Separated the base classes into individual libraries. + +1.0.0 +17.10.2010 +First release diff --git a/third_party/QtWebApp/LICENSE.txt b/third_party/QtWebApp/LICENSE.txt new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/third_party/QtWebApp/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/third_party/QtWebApp/README.txt b/third_party/QtWebApp/README.txt new file mode 100644 index 00000000..50c73a34 --- /dev/null +++ b/third_party/QtWebApp/README.txt @@ -0,0 +1,25 @@ +QtWebAppLib is a library to develop server-side web applications in C++. +It requires the Qt SDK version 4.7.0 or newer. + +License: LGPL v3. + +Project homepage: http://stefanfrings.de/qtwebapp/index.html +Tutorial: http://stefanfrings.de/qtwebapp/tutorial/index.html +API doc: http://stefanfrings.de/qtwebapp/api/index.html + +There are three demo applications that demonstrate how to use the library. + +Demo1 shows how to use the library by including the source code into your +project. This does not depend on the shared library. + +Demo2 shows how to link against the shared library. +Build the project QtWebApp to generate the shared library. + +Demo3 shows how to use the qtservice component to start the application +as a Windows Service or Unix daemon. Start it with option -h to get help. + +I recommend to include the library by source as shown in Demo1 and 3. + +Stefan Frings +http://stefanfrings.de + diff --git a/third_party/QtWebApp/httpserver/httpconnectionhandler.cpp b/third_party/QtWebApp/httpserver/httpconnectionhandler.cpp index 6e2150fa..934f59d0 100644 --- a/third_party/QtWebApp/httpserver/httpconnectionhandler.cpp +++ b/third_party/QtWebApp/httpserver/httpconnectionhandler.cpp @@ -6,8 +6,10 @@ #include "httpconnectionhandler.h" #include "httpresponse.h" -HttpConnectionHandler::HttpConnectionHandler(QSettings* settings, HttpRequestHandler* requestHandler, QSslConfiguration* sslConfiguration) - : QThread() +using namespace stefanfrings; + +HttpConnectionHandler::HttpConnectionHandler(const QSettings *settings, HttpRequestHandler *requestHandler, const QSslConfiguration* sslConfiguration) + : QObject() { Q_ASSERT(settings!=nullptr); Q_ASSERT(requestHandler!=nullptr); @@ -17,43 +19,56 @@ HttpConnectionHandler::HttpConnectionHandler(QSettings* settings, HttpRequestHan currentRequest=nullptr; busy=false; - // Create TCP or SSL socket - createSocket(); + // execute signals in a new thread + thread = new QThread(); + thread->start(); + qDebug("HttpConnectionHandler (%p): thread started", static_cast(this)); + moveToThread(thread); + readTimer.moveToThread(thread); + readTimer.setSingleShot(true); - // execute signals in my own thread - moveToThread(this); - socket->moveToThread(this); - readTimer.moveToThread(this); + // Create TCP or SSL socket + createSocket(); + socket->moveToThread(thread); // Connect signals connect(socket, SIGNAL(readyRead()), SLOT(read())); connect(socket, SIGNAL(disconnected()), SLOT(disconnected())); connect(&readTimer, SIGNAL(timeout()), SLOT(readTimeout())); - readTimer.setSingleShot(true); + connect(thread, SIGNAL(finished()), this, SLOT(thread_done())); - qDebug("HttpConnectionHandler (%p): constructed", this); - this->start(); + qDebug("HttpConnectionHandler (%p): constructed", static_cast(this)); +} + + +void HttpConnectionHandler::thread_done() +{ + readTimer.stop(); + socket->close(); + delete socket; + qDebug("HttpConnectionHandler (%p): thread stopped", static_cast(this)); } HttpConnectionHandler::~HttpConnectionHandler() { - quit(); - wait(); - qDebug("HttpConnectionHandler (%p): destroyed", this); + thread->quit(); + thread->wait(); + thread->deleteLater(); + qDebug("HttpConnectionHandler (%p): destroyed", static_cast(this)); } void HttpConnectionHandler::createSocket() { // If SSL is supported and configured, then create an instance of QSslSocket - #ifndef QT_NO_OPENSSL + #ifndef QT_NO_SSL if (sslConfiguration) { QSslSocket* sslSocket=new QSslSocket(); sslSocket->setSslConfiguration(*sslConfiguration); socket=sslSocket; - qDebug("HttpConnectionHandler (%p): SSL is enabled", this); + qDebug("HttpConnectionHandler (%p): SSL is enabled", static_cast(this)); return; } #endif @@ -62,27 +77,9 @@ void HttpConnectionHandler::createSocket() } -void HttpConnectionHandler::run() -{ - qDebug("HttpConnectionHandler (%p): thread started", this); - try - { - exec(); - } - catch (...) - { - qCritical("HttpConnectionHandler (%p): an uncatched exception occured in the thread",this); - } - socket->close(); - delete socket; - readTimer.stop(); - qDebug("HttpConnectionHandler (%p): thread stopped", this); -} - - void HttpConnectionHandler::handleConnection(tSocketDescriptor socketDescriptor) { - qDebug("HttpConnectionHandler (%p): handle new connection", this); + qDebug("HttpConnectionHandler (%p): handle new connection", static_cast(this)); busy = true; Q_ASSERT(socket->isOpen()==false); // if not, then the handler is already busy @@ -93,16 +90,17 @@ void HttpConnectionHandler::handleConnection(tSocketDescriptor socketDescriptor) if (!socket->setSocketDescriptor(socketDescriptor)) { - qCritical("HttpConnectionHandler (%p): cannot initialize socket: %s", this,qPrintable(socket->errorString())); + qCritical("HttpConnectionHandler (%p): cannot initialize socket: %s", + static_cast(this),qPrintable(socket->errorString())); return; } - #ifndef QT_NO_OPENSSL + #ifndef QT_NO_SSL // Switch on encryption, if SSL is configured if (sslConfiguration) { - qDebug("HttpConnectionHandler (%p): Starting encryption", this); - ((QSslSocket*)socket)->startServerEncryption(); + qDebug("HttpConnectionHandler (%p): Starting encryption", static_cast(this)); + (static_cast(socket))->startServerEncryption(); } #endif @@ -128,12 +126,12 @@ void HttpConnectionHandler::setBusy() void HttpConnectionHandler::readTimeout() { - qDebug("HttpConnectionHandler (%p): read timeout occured",this); + qDebug("HttpConnectionHandler (%p): read timeout occured",static_cast(this)); //Commented out because QWebView cannot handle this. //socket->write("HTTP/1.1 408 request timeout\r\nConnection: close\r\n\r\n408 request timeout\r\n"); - socket->flush(); + while(socket->bytesToWrite()) socket->waitForBytesWritten(); socket->disconnectFromHost(); delete currentRequest; currentRequest=nullptr; @@ -142,7 +140,7 @@ void HttpConnectionHandler::readTimeout() void HttpConnectionHandler::disconnected() { - qDebug("HttpConnectionHandler (%p): disconnected", this); + qDebug("HttpConnectionHandler (%p): disconnected", static_cast(this)); socket->close(); readTimer.stop(); busy = false; @@ -154,7 +152,7 @@ void HttpConnectionHandler::read() while (socket->bytesAvailable()) { #ifdef SUPERVERBOSE - qDebug("HttpConnectionHandler (%p): read input",this); + qDebug("HttpConnectionHandler (%p): read input",static_cast(this)); #endif // Create new HttpRequest object if necessary @@ -180,7 +178,7 @@ void HttpConnectionHandler::read() if (currentRequest->getStatus()==HttpRequest::abort) { socket->write("HTTP/1.1 413 entity too large\r\nConnection: close\r\n\r\n413 Entity too large\r\n"); - socket->flush(); + while(socket->bytesToWrite()) socket->waitForBytesWritten(); socket->disconnectFromHost(); delete currentRequest; currentRequest=nullptr; @@ -191,7 +189,7 @@ void HttpConnectionHandler::read() if (currentRequest->getStatus()==HttpRequest::complete) { readTimer.stop(); - qDebug("HttpConnectionHandler (%p): received request",this); + qDebug("HttpConnectionHandler (%p): received request",static_cast(this)); // Copy the Connection:close header to the response HttpResponse response(socket); @@ -220,7 +218,8 @@ void HttpConnectionHandler::read() } catch (...) { - qCritical("HttpConnectionHandler (%p): An uncatched exception occured in the request handler",this); + qCritical("HttpConnectionHandler (%p): An uncatched exception occured in the request handler", + static_cast(this)); } // Finalize sending the response if not already done @@ -229,7 +228,7 @@ void HttpConnectionHandler::read() response.write(QByteArray(),true); } - qDebug("HttpConnectionHandler (%p): finished request",this); + qDebug("HttpConnectionHandler (%p): finished request",static_cast(this)); // Find out whether the connection must be closed if (!closeConnection) @@ -259,7 +258,7 @@ void HttpConnectionHandler::read() // Close the connection or prepare for the next request on the same connection. if (closeConnection) { - socket->flush(); + while(socket->bytesToWrite()) socket->waitForBytesWritten(); socket->disconnectFromHost(); } else diff --git a/third_party/QtWebApp/httpserver/httpconnectionhandler.h b/third_party/QtWebApp/httpserver/httpconnectionhandler.h index d32c4451..bf9bffac 100644 --- a/third_party/QtWebApp/httpserver/httpconnectionhandler.h +++ b/third_party/QtWebApp/httpserver/httpconnectionhandler.h @@ -6,7 +6,7 @@ #ifndef HTTPCONNECTIONHANDLER_H #define HTTPCONNECTIONHANDLER_H -#ifndef QT_NO_OPENSSL +#ifndef QT_NO_SSL #include #endif #include @@ -17,6 +17,8 @@ #include "httprequest.h" #include "httprequesthandler.h" +namespace stefanfrings { + /** Alias type definition, for compatibility to different Qt versions */ #if QT_VERSION >= 0x050000 typedef qintptr tSocketDescriptor; @@ -25,7 +27,7 @@ #endif /** Alias for QSslConfiguration if OpenSSL is not supported */ -#ifdef QT_NO_OPENSSL +#ifdef QT_NO_SSL #define QSslConfiguration QObject #endif @@ -44,7 +46,7 @@ The readTimeout value defines the maximum time to wait for a complete HTTP request. @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize. */ -class DECLSPEC HttpConnectionHandler : public QThread { +class DECLSPEC HttpConnectionHandler : public QObject { Q_OBJECT Q_DISABLE_COPY(HttpConnectionHandler) @@ -56,7 +58,8 @@ public: @param requestHandler Handler that will process each incoming HTTP request @param sslConfiguration SSL (HTTPS) will be used if not NULL */ - HttpConnectionHandler(QSettings* settings, HttpRequestHandler* requestHandler, QSslConfiguration* sslConfiguration=nullptr); + HttpConnectionHandler(const QSettings* settings, HttpRequestHandler* requestHandler, + const QSslConfiguration* sslConfiguration=nullptr); /** Destructor */ virtual ~HttpConnectionHandler(); @@ -70,11 +73,14 @@ public: private: /** Configuration settings */ - QSettings* settings; + const QSettings* settings; /** TCP socket of the current connection */ QTcpSocket* socket; + /** The thread that processes events of this connection */ + QThread* thread; + /** Time for read timeout detection */ QTimer readTimer; @@ -88,10 +94,7 @@ private: bool busy; /** Configuration for SSL */ - QSslConfiguration* sslConfiguration; - - /** Executes the threads own event loop */ - void run() override; + const QSslConfiguration* sslConfiguration; /** Create SSL or TCP socket */ void createSocket(); @@ -102,7 +105,7 @@ public slots: Received from from the listener, when the handler shall start processing a new connection. @param socketDescriptor references the accepted connection. */ - void handleConnection(tSocketDescriptor socketDescriptor); + void handleConnection(const tSocketDescriptor socketDescriptor); private slots: @@ -115,6 +118,10 @@ private slots: /** Received from the socket when a connection has been closed */ void disconnected(); + /** Cleanup after the thread is closed */ + void thread_done(); }; +} // end of namespace + #endif // HTTPCONNECTIONHANDLER_H diff --git a/third_party/QtWebApp/httpserver/httpconnectionhandlerpool.cpp b/third_party/QtWebApp/httpserver/httpconnectionhandlerpool.cpp index be4a85fa..112e1c8f 100644 --- a/third_party/QtWebApp/httpserver/httpconnectionhandlerpool.cpp +++ b/third_party/QtWebApp/httpserver/httpconnectionhandlerpool.cpp @@ -1,4 +1,4 @@ -#ifndef QT_NO_OPENSSL +#ifndef QT_NO_SSL #include #include #include @@ -7,13 +7,15 @@ #include #include "httpconnectionhandlerpool.h" -HttpConnectionHandlerPool::HttpConnectionHandlerPool(QSettings* settings, HttpRequestHandler* requestHandler) +using namespace stefanfrings; + +HttpConnectionHandlerPool::HttpConnectionHandlerPool(const QSettings *settings, HttpRequestHandler *requestHandler) : QObject() { - Q_ASSERT(settings!=nullptr); + Q_ASSERT(settings!=0); this->settings=settings; this->requestHandler=requestHandler; - this->sslConfiguration=nullptr; + this->sslConfiguration=NULL; loadSslConfig(); cleanupTimer.start(settings->value("cleanupInterval",1000).toInt()); connect(&cleanupTimer, SIGNAL(timeout()), SLOT(cleanup())); @@ -34,7 +36,7 @@ HttpConnectionHandlerPool::~HttpConnectionHandlerPool() HttpConnectionHandler* HttpConnectionHandlerPool::getConnectionHandler() { - HttpConnectionHandler* freeHandler=nullptr; + HttpConnectionHandler* freeHandler=0; mutex.lock(); // find a free handler in pool foreach(HttpConnectionHandler* handler, pool) @@ -91,7 +93,7 @@ void HttpConnectionHandlerPool::loadSslConfig() QString sslCertFileName=settings->value("sslCertFile","").toString(); if (!sslKeyFileName.isEmpty() && !sslCertFileName.isEmpty()) { - #ifdef QT_NO_OPENSSL + #ifdef QT_NO_SSL qWarning("HttpConnectionHandlerPool: SSL is not supported"); #else // Convert relative fileNames to absolute, based on the directory of the config file. diff --git a/third_party/QtWebApp/httpserver/httpconnectionhandlerpool.h b/third_party/QtWebApp/httpserver/httpconnectionhandlerpool.h index a9b9908b..3e556a56 100644 --- a/third_party/QtWebApp/httpserver/httpconnectionhandlerpool.h +++ b/third_party/QtWebApp/httpserver/httpconnectionhandlerpool.h @@ -8,6 +8,8 @@ #include "httpglobal.h" #include "httpconnectionhandler.h" +namespace stefanfrings { + /** Pool of http connection handlers. The size of the pool grows and shrinks on demand. @@ -54,7 +56,7 @@ public: @param requestHandler The handler that will process each received HTTP request. @warning The requestMapper gets deleted by the destructor of this pool */ - HttpConnectionHandlerPool(QSettings* settings, HttpRequestHandler* requestHandler); + HttpConnectionHandlerPool(const QSettings* settings, HttpRequestHandler *requestHandler); /** Destructor */ virtual ~HttpConnectionHandlerPool(); @@ -65,7 +67,7 @@ public: private: /** Settings for this pool */ - QSettings* settings; + const QSettings* settings; /** Will be assigned to each Connectionhandler during their creation */ HttpRequestHandler* requestHandler; @@ -92,4 +94,6 @@ private slots: }; +} // end of namespace + #endif // HTTPCONNECTIONHANDLERPOOL_H diff --git a/third_party/QtWebApp/httpserver/httpcookie.cpp b/third_party/QtWebApp/httpserver/httpcookie.cpp index 77d2f2aa..f09a2c37 100644 --- a/third_party/QtWebApp/httpserver/httpcookie.cpp +++ b/third_party/QtWebApp/httpserver/httpcookie.cpp @@ -5,6 +5,8 @@ #include "httpcookie.h" +using namespace stefanfrings; + HttpCookie::HttpCookie() { version=1; diff --git a/third_party/QtWebApp/httpserver/httpcookie.h b/third_party/QtWebApp/httpserver/httpcookie.h index 70d455a9..f649f15f 100644 --- a/third_party/QtWebApp/httpserver/httpcookie.h +++ b/third_party/QtWebApp/httpserver/httpcookie.h @@ -10,6 +10,8 @@ #include #include "httpglobal.h" +namespace stefanfrings { + /** HTTP cookie as defined in RFC 2109. This class can also parse RFC 2965 cookies, but skips fields that are not defined in RFC @@ -34,7 +36,10 @@ public: @param secure If true, the cookie will be sent by the browser to the server only on secure connections @param httpOnly If true, the browser does not allow client-side scripts to access the cookie */ - HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path="/", const QByteArray comment=QByteArray(), const QByteArray domain=QByteArray(), const bool secure=false, const bool httpOnly=false); + HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, + const QByteArray path="/", const QByteArray comment=QByteArray(), + const QByteArray domain=QByteArray(), const bool secure=false, + const bool httpOnly=false); /** Create a cookie from a string. @@ -72,7 +77,7 @@ public: /** Set secure mode, so that the cookie will be sent by the browser to the server only on secure connections */ void setSecure(const bool secure); - /** Set secure mode, so that he browser does not allow client-side scripts to access the cookie */ + /** Set HTTP-only mode, so that he browser does not allow client-side scripts to access the cookie */ void setHttpOnly(const bool httpOnly); /** Get the name of this cookie */ @@ -87,7 +92,7 @@ public: /** Get the domain of this cookie */ QByteArray getDomain() const; - /** Set the maximum age of this cookie in seconds. */ + /** Get the maximum age of this cookie in seconds. */ int getMaxAge() const; /** Set the path of this cookie */ @@ -96,7 +101,7 @@ public: /** Get the secure flag of this cookie */ bool getSecure() const; - /** Get the httpOnly of this cookie */ + /** Get the HTTP-only flag of this cookie */ bool getHttpOnly() const; /** Returns always 1 */ @@ -116,4 +121,6 @@ private: }; +} // end of namespace + #endif // HTTPCOOKIE_H diff --git a/third_party/QtWebApp/httpserver/httpglobal.cpp b/third_party/QtWebApp/httpserver/httpglobal.cpp index 088dab14..d938a95c 100644 --- a/third_party/QtWebApp/httpserver/httpglobal.cpp +++ b/third_party/QtWebApp/httpserver/httpglobal.cpp @@ -2,6 +2,6 @@ const char* getQtWebAppLibVersion() { - return "1.6.5"; + return "1.7.11"; } diff --git a/third_party/QtWebApp/httpserver/httpglobal.h b/third_party/QtWebApp/httpserver/httpglobal.h index 4f499871..e7e856a9 100644 --- a/third_party/QtWebApp/httpserver/httpglobal.h +++ b/third_party/QtWebApp/httpserver/httpglobal.h @@ -23,5 +23,6 @@ /** Get the library version number */ DECLSPEC const char* getQtWebAppLibVersion(); + #endif // HTTPGLOBAL_H diff --git a/third_party/QtWebApp/httpserver/httplistener.cpp b/third_party/QtWebApp/httpserver/httplistener.cpp index 14d861ad..74871584 100644 --- a/third_party/QtWebApp/httpserver/httplistener.cpp +++ b/third_party/QtWebApp/httpserver/httplistener.cpp @@ -8,7 +8,9 @@ #include "httpconnectionhandlerpool.h" #include -HttpListener::HttpListener(QSettings* settings, HttpRequestHandler* requestHandler, QObject *parent) +using namespace stefanfrings; + +HttpListener::HttpListener(const QSettings* settings, HttpRequestHandler* requestHandler, QObject *parent) : QTcpServer(parent) { Q_ASSERT(settings!=nullptr); @@ -37,21 +39,11 @@ void HttpListener::listen() pool=new HttpConnectionHandlerPool(settings,requestHandler); } QString host = settings->value("host").toString(); - int port=settings->value("port").toInt(); + quint16 port=settings->value("port").toUInt() & 0xFFFF; QTcpServer::listen(host.isEmpty() ? QHostAddress::Any : QHostAddress(host), port); - - //YACReader--------------------------------------------- - //try to listen even if the default port is no available - int i = 0; - while (!isListening() && i < 1000) { - QTcpServer::listen(QHostAddress::Any, (rand() % 45535)+20000); - i++; - } - //------------------------------------------------------ - if (!isListening()) { - qDebug("HttpListener: Cannot bind on port %i: %s",port,qPrintable(errorString())); + qCritical("HttpListener: Cannot bind on port %i: %s",port,qPrintable(errorString())); } else { qDebug("HttpListener: Listening on port %i",port); @@ -82,17 +74,14 @@ void HttpListener::incomingConnection(tSocketDescriptor socketDescriptor) { // Let the handler process the new connection. if (freeHandler) { - // The descriptor is passed via signal/slot because the handler lives in another - // thread and cannot open the socket when directly called by another thread. - connect(this,SIGNAL(handleConnection(tSocketDescriptor)),freeHandler,SLOT(handleConnection(tSocketDescriptor))); - emit handleConnection(socketDescriptor); - disconnect(this,SIGNAL(handleConnection(tSocketDescriptor)),freeHandler,SLOT(handleConnection(tSocketDescriptor))); + // The descriptor is passed via event queue because the handler lives in another thread + QMetaObject::invokeMethod(freeHandler, "handleConnection", Qt::QueuedConnection, Q_ARG(tSocketDescriptor, socketDescriptor)); } else { // Reject the connection qDebug("HttpListener: Too many incoming connections"); - auto* socket=new QTcpSocket(this); + QTcpSocket* socket=new QTcpSocket(this); socket->setSocketDescriptor(socketDescriptor); connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); socket->write("HTTP/1.1 503 too many connections\r\nConnection: close\r\n\r\nToo many connections\r\n"); diff --git a/third_party/QtWebApp/httpserver/httplistener.h b/third_party/QtWebApp/httpserver/httplistener.h index 7bd49308..e5794f2f 100644 --- a/third_party/QtWebApp/httpserver/httplistener.h +++ b/third_party/QtWebApp/httpserver/httplistener.h @@ -14,6 +14,8 @@ #include "httpconnectionhandlerpool.h" #include "httprequesthandler.h" +namespace stefanfrings { + /** Listens for incoming TCP connections and and passes all incoming HTTP requests to your implementation of HttpRequestHandler, which processes the request and generates the response (usually a HTML document). @@ -47,12 +49,17 @@ public: /** Constructor. Creates a connection pool and starts listening on the configured host and port. - @param settings Configuration settings for the HTTP server. Must not be 0. + @param settings Configuration settings, usually stored in an INI file. Must not be 0. + Settings are read from the current group, so the caller must have called settings->beginGroup(). + Because the group must not change during runtime, it is recommended to provide a + separate QSettings instance that is not used by other parts of the program. + The HttpListener does not take over ownership of the QSettings instance, so the + caller should destroy it during shutdown. @param requestHandler Processes each received HTTP request, usually by dispatching to controller classes. @param parent Parent object. @warning Ensure to close or delete the listener before deleting the request handler. */ - HttpListener(QSettings* settings, HttpRequestHandler* requestHandler, QObject* parent = nullptr); + HttpListener(const QSettings* settings, HttpRequestHandler* requestHandler, QObject* parent=nullptr); /** Destructor */ virtual ~HttpListener(); @@ -71,12 +78,12 @@ public: protected: /** Serves new incoming connection requests */ - void incomingConnection(tSocketDescriptor socketDescriptor) override; + void incomingConnection(tSocketDescriptor socketDescriptor); private: /** Configuration settings for the HTTP server */ - QSettings* settings; + const QSettings* settings; /** Point to the reuqest handler which processes all HTTP requests */ HttpRequestHandler* requestHandler; @@ -95,4 +102,6 @@ signals: }; +} // end of namespace + #endif // HTTPLISTENER_H diff --git a/third_party/QtWebApp/httpserver/httprequest.cpp b/third_party/QtWebApp/httpserver/httprequest.cpp index 00a0617a..75642b90 100644 --- a/third_party/QtWebApp/httpserver/httprequest.cpp +++ b/third_party/QtWebApp/httpserver/httprequest.cpp @@ -8,24 +8,29 @@ #include #include "httpcookie.h" -HttpRequest::HttpRequest(QSettings* settings) +using namespace stefanfrings; + +HttpRequest::HttpRequest(const QSettings* settings) { status=waitForRequest; currentSize=0; expectedBodySize=0; maxSize=settings->value("maxRequestSize","16000").toInt(); maxMultiPartSize=settings->value("maxMultiPartSize","1000000").toInt(); + tempFile=nullptr; } + void HttpRequest::readRequest(QTcpSocket* socket) { #ifdef SUPERVERBOSE qDebug("HttpRequest: read request"); #endif int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow - lineBuffer.append(socket->readLine(toRead)); - currentSize+=lineBuffer.size(); - if (!lineBuffer.contains('\r') && !lineBuffer.contains('\n')) + QByteArray dataRead = socket->readLine(toRead); + currentSize += dataRead.size(); + lineBuffer.append(dataRead); + if (!lineBuffer.contains("\r\n")) { #ifdef SUPERVERBOSE qDebug("HttpRequest: collecting more parts until line break"); @@ -36,13 +41,15 @@ void HttpRequest::readRequest(QTcpSocket* socket) lineBuffer.clear(); if (!newData.isEmpty()) { + qDebug("HttpRequest: from %s: %s",qPrintable(socket->peerAddress().toString()),newData.data()); QList list=newData.split(' '); if (list.count()!=3 || !list.at(2).contains("HTTP")) { qWarning("HttpRequest: received broken HTTP request, invalid first line"); status=abort; } - else { + else + { method=list.at(0).trimmed(); path=list.at(1); version=list.at(2); @@ -54,13 +61,11 @@ void HttpRequest::readRequest(QTcpSocket* socket) void HttpRequest::readHeader(QTcpSocket* socket) { - #ifdef SUPERVERBOSE - qDebug("HttpRequest: read header"); - #endif int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow - lineBuffer.append(socket->readLine(toRead)); - currentSize+=lineBuffer.size(); - if (!lineBuffer.contains('\r') && !lineBuffer.contains('\n')) + QByteArray dataRead = socket->readLine(toRead); + currentSize += dataRead.size(); + lineBuffer.append(dataRead); + if (!lineBuffer.contains("\r\n")) { #ifdef SUPERVERBOSE qDebug("HttpRequest: collecting more parts until line break"); @@ -166,18 +171,23 @@ void HttpRequest::readBody(QTcpSocket* socket) #ifdef SUPERVERBOSE qDebug("HttpRequest: receiving multipart body"); #endif - if (!tempFile.isOpen()) + // Create an object for the temporary file, if not already present + if (tempFile == nullptr) { - tempFile.open(); + tempFile = new QTemporaryFile; + } + if (!tempFile->isOpen()) + { + tempFile->open(); } // Transfer data in 64kb blocks - int fileSize=tempFile.size(); - int toRead=expectedBodySize-fileSize; + qint64 fileSize=tempFile->size(); + qint64 toRead=expectedBodySize-fileSize; if (toRead>65536) { toRead=65536; } - fileSize+=tempFile.write(socket->read(toRead)); + fileSize+=tempFile->write(socket->read(toRead)); if (fileSize>=maxMultiPartSize) { qWarning("HttpRequest: received too many multipart bytes"); @@ -188,13 +198,13 @@ void HttpRequest::readBody(QTcpSocket* socket) #ifdef SUPERVERBOSE qDebug("HttpRequest: received whole multipart body"); #endif - tempFile.flush(); - if (tempFile.error()) + tempFile->flush(); + if (tempFile->error()) { qCritical("HttpRequest: Error writing temp file for multipart body"); } parseMultiPartFile(); - tempFile.close(); + tempFile->close(); status=complete; } } @@ -381,10 +391,11 @@ QByteArray HttpRequest::urlDecode(const QByteArray source) while (percentChar>=0) { bool ok; - char byte=buffer.mid(percentChar+1,2).toInt(&ok,16); + int hexCode=buffer.mid(percentChar+1,2).toInt(&ok,16); if (ok) { - buffer.replace(percentChar,3,(char*)&byte,1); + char c=char(hexCode); + buffer.replace(percentChar,3,&c,1); } percentChar=buffer.indexOf('%',percentChar+1); } @@ -395,18 +406,18 @@ QByteArray HttpRequest::urlDecode(const QByteArray source) void HttpRequest::parseMultiPartFile() { qDebug("HttpRequest: parsing multipart temp file"); - tempFile.seek(0); + tempFile->seek(0); bool finished=false; - while (!tempFile.atEnd() && !finished && !tempFile.error()) + while (!tempFile->atEnd() && !finished && !tempFile->error()) { #ifdef SUPERVERBOSE qDebug("HttpRequest: reading multpart headers"); #endif QByteArray fieldName; QByteArray fileName; - while (!tempFile.atEnd() && !finished && !tempFile.error()) + while (!tempFile->atEnd() && !finished && !tempFile->error()) { - QByteArray line=tempFile.readLine(65536).trimmed(); + QByteArray line=tempFile->readLine(65536).trimmed(); if (line.startsWith("Content-Disposition:")) { if (line.contains("form-data")) @@ -443,9 +454,9 @@ void HttpRequest::parseMultiPartFile() #endif QTemporaryFile* uploadedFile=nullptr; QByteArray fieldValue; - while (!tempFile.atEnd() && !finished && !tempFile.error()) + while (!tempFile->atEnd() && !finished && !tempFile->error()) { - QByteArray line=tempFile.readLine(65536); + QByteArray line=tempFile->readLine(65536); if (line.startsWith("--"+boundary)) { // Boundary found. Until now we have collected 2 bytes too much, @@ -471,7 +482,7 @@ void HttpRequest::parseMultiPartFile() parameters.insert(fieldName,fileName); qDebug("HttpRequest: set parameter %s=%s",fieldName.data(),fileName.data()); uploadedFiles.insert(fieldName,uploadedFile); - qDebug("HttpRequest: uploaded file size is %i",(int) uploadedFile->size()); + qDebug("HttpRequest: uploaded file size is %lli",uploadedFile->size()); } else { @@ -509,9 +520,9 @@ void HttpRequest::parseMultiPartFile() } } } - if (tempFile.error()) + if (tempFile->error()) { - qCritical("HttpRequest: cannot read temp file, %s",qPrintable(tempFile.errorString())); + qCritical("HttpRequest: cannot read temp file, %s",qPrintable(tempFile->errorString())); } #ifdef SUPERVERBOSE qDebug("HttpRequest: finished parsing multipart temp file"); @@ -523,9 +534,20 @@ HttpRequest::~HttpRequest() foreach(QByteArray key, uploadedFiles.keys()) { QTemporaryFile* file=uploadedFiles.value(key); - file->close(); + if (file->isOpen()) + { + file->close(); + } delete file; } + if (tempFile != nullptr) + { + if (tempFile->isOpen()) + { + tempFile->close(); + } + delete tempFile; + } } QTemporaryFile* HttpRequest::getUploadedFile(const QByteArray fieldName) const @@ -553,3 +575,4 @@ QHostAddress HttpRequest::getPeerAddress() const { return peerAddress; } + diff --git a/third_party/QtWebApp/httpserver/httprequest.h b/third_party/QtWebApp/httpserver/httprequest.h index c13846af..28e6ac30 100644 --- a/third_party/QtWebApp/httpserver/httprequest.h +++ b/third_party/QtWebApp/httpserver/httprequest.h @@ -16,6 +16,8 @@ #include #include "httpglobal.h" +namespace stefanfrings { + /** This object represents a single HTTP request. It reads the request from a TCP socket and provides getters for the individual parts @@ -46,7 +48,7 @@ public: Constructor. @param settings Configuration settings */ - HttpRequest(QSettings* settings); + HttpRequest(const QSettings* settings); /** Destructor. @@ -59,7 +61,7 @@ public: until the status is RequestStatus::complete or RequestStatus::abort. @param socket Source of the data */ - void readFromSocket(QTcpSocket* socket); + void readFromSocket(QTcpSocket *socket); /** Get the status of this reqeust. @@ -207,9 +209,9 @@ private: QByteArray boundary; /** Temp file, that is used to store the multipart/form-data body */ - QTemporaryFile tempFile; + QTemporaryFile* tempFile; - /** Parset he multipart body, that has been stored in the temp file. */ + /** Parse the multipart body, that has been stored in the temp file. */ void parseMultiPartFile(); /** Sub-procedure of readFromSocket(), read the first line of a request. */ @@ -232,4 +234,6 @@ private: }; +} // end of namespace + #endif // HTTPREQUEST_H diff --git a/third_party/QtWebApp/httpserver/httprequesthandler.cpp b/third_party/QtWebApp/httpserver/httprequesthandler.cpp index 879ccd4f..f3a5fbe7 100644 --- a/third_party/QtWebApp/httpserver/httprequesthandler.cpp +++ b/third_party/QtWebApp/httpserver/httprequesthandler.cpp @@ -5,6 +5,8 @@ #include "httprequesthandler.h" +using namespace stefanfrings; + HttpRequestHandler::HttpRequestHandler(QObject* parent) : QObject(parent) {} diff --git a/third_party/QtWebApp/httpserver/httprequesthandler.h b/third_party/QtWebApp/httpserver/httprequesthandler.h index 913cc3ca..ea24612a 100644 --- a/third_party/QtWebApp/httpserver/httprequesthandler.h +++ b/third_party/QtWebApp/httpserver/httprequesthandler.h @@ -10,6 +10,8 @@ #include "httprequest.h" #include "httpresponse.h" +namespace stefanfrings { + /** The request handler generates a response for each HTTP request. Web Applications usually have one central request handler that maps incoming requests to several @@ -46,4 +48,6 @@ public: }; +} // end of namespace + #endif // HTTPREQUESTHANDLER_H diff --git a/third_party/QtWebApp/httpserver/httpresponse.cpp b/third_party/QtWebApp/httpserver/httpresponse.cpp index 7dbb600a..a2afba89 100644 --- a/third_party/QtWebApp/httpserver/httpresponse.cpp +++ b/third_party/QtWebApp/httpserver/httpresponse.cpp @@ -5,7 +5,9 @@ #include "httpresponse.h" -HttpResponse::HttpResponse(QTcpSocket* socket) +using namespace stefanfrings; + +HttpResponse::HttpResponse(QTcpSocket *socket) { this->socket=socket; statusCode=200; @@ -67,6 +69,7 @@ void HttpResponse::writeHeaders() } buffer.append("\r\n"); writeToSocket(buffer); + socket->flush(); sentHeaders=true; } @@ -82,7 +85,7 @@ bool HttpResponse::writeToSocket(QByteArray data) socket->waitForBytesWritten(-1); } - int written=socket->write(ptr,remaining); + qint64 written=socket->write(ptr,remaining); if (written==-1) { return false; diff --git a/third_party/QtWebApp/httpserver/httpresponse.h b/third_party/QtWebApp/httpserver/httpresponse.h index 44054806..62050d02 100644 --- a/third_party/QtWebApp/httpserver/httpresponse.h +++ b/third_party/QtWebApp/httpserver/httpresponse.h @@ -12,6 +12,8 @@ #include "httpglobal.h" #include "httpcookie.h" +namespace stefanfrings { + /** This object represents a HTTP response, used to return something to the web client.

@@ -39,7 +41,7 @@ public: Constructor. @param socket used to write the response */ - HttpResponse(QTcpSocket* socket); + HttpResponse(QTcpSocket *socket); /** Set a HTTP response header. @@ -47,7 +49,7 @@ public: @param name name of the header @param value value of the header */ - void setHeader(QByteArray name, QByteArray value); + void setHeader(const QByteArray name, const QByteArray value); /** Set a HTTP response header. @@ -55,7 +57,7 @@ public: @param name name of the header @param value value of the header */ - void setHeader(QByteArray name, int value); + void setHeader(const QByteArray name, const int value); /** Get the map of HTTP response headers */ QMap& getHeaders(); @@ -67,7 +69,7 @@ public: Set status code and description. The default is 200,OK. You must call this method before the first write(). */ - void setStatus(int statusCode, QByteArray description=QByteArray()); + void setStatus(const int statusCode, const QByteArray description=QByteArray()); /** Return the status code. */ int getStatusCode() const; @@ -85,7 +87,7 @@ public: @param data Data bytes of the body @param lastPart Indicates that this is the last chunk of data and flushes the output buffer. */ - void write(QByteArray data, bool lastPart=false); + void write(const QByteArray data, const bool lastPart=false); /** Indicates whether the body has been sent completely (write() has been called with lastPart=true). @@ -156,4 +158,6 @@ private: }; +} // end of namespace + #endif // HTTPRESPONSE_H diff --git a/third_party/QtWebApp/httpserver/httpsession.cpp b/third_party/QtWebApp/httpserver/httpsession.cpp index b3977730..4337082b 100644 --- a/third_party/QtWebApp/httpserver/httpsession.cpp +++ b/third_party/QtWebApp/httpserver/httpsession.cpp @@ -7,6 +7,7 @@ #include #include +using namespace stefanfrings; HttpSession::HttpSession(bool canStore) { @@ -17,7 +18,7 @@ HttpSession::HttpSession(bool canStore) dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); dataPtr->id=QUuid::createUuid().toString().toLocal8Bit(); #ifdef SUPERVERBOSE - qDebug("HttpSession: created new session data with id %s",dataPtr->id.data()); + qDebug("HttpSession: (constructor) new session %s with refCount=1",dataPtr->id.constData()); #endif } else @@ -34,7 +35,7 @@ HttpSession::HttpSession(const HttpSession& other) dataPtr->lock.lockForWrite(); dataPtr->refCount++; #ifdef SUPERVERBOSE - qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); + qDebug("HttpSession: (constructor) copy session %s refCount=%i",dataPtr->id.constData(),dataPtr->refCount); #endif dataPtr->lock.unlock(); } @@ -49,7 +50,7 @@ HttpSession& HttpSession::operator= (const HttpSession& other) dataPtr->lock.lockForWrite(); dataPtr->refCount++; #ifdef SUPERVERBOSE - qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); + qDebug("HttpSession: (operator=) session %s refCount=%i",dataPtr->id.constData(),dataPtr->refCount); #endif dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); dataPtr->lock.unlock(); @@ -57,14 +58,15 @@ HttpSession& HttpSession::operator= (const HttpSession& other) if (oldPtr) { int refCount; - oldPtr->lock.lockForRead(); - refCount=oldPtr->refCount--; + oldPtr->lock.lockForWrite(); + refCount=--oldPtr->refCount; #ifdef SUPERVERBOSE - qDebug("HttpSession: refCount of %s is %i",oldPtr->id.data(),oldPtr->refCount); + qDebug("HttpSession: (operator=) session %s refCount=%i",oldPtr->id.constData(),oldPtr->refCount); #endif oldPtr->lock.unlock(); if (refCount==0) { + qDebug("HttpSession: deleting old data"); delete oldPtr; } } @@ -75,10 +77,10 @@ HttpSession::~HttpSession() { if (dataPtr) { int refCount; - dataPtr->lock.lockForRead(); + dataPtr->lock.lockForWrite(); refCount=--dataPtr->refCount; #ifdef SUPERVERBOSE - qDebug("HttpSession: refCount of %s is %i",dataPtr->id.data(),dataPtr->refCount); + qDebug("HttpSession: (destructor) session %s refCount=%i",dataPtr->id.constData(),dataPtr->refCount); #endif dataPtr->lock.unlock(); if (refCount==0) @@ -179,7 +181,7 @@ void HttpSession::setLastAccess() { if (dataPtr) { - dataPtr->lock.lockForRead(); + dataPtr->lock.lockForWrite(); dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); dataPtr->lock.unlock(); } diff --git a/third_party/QtWebApp/httpserver/httpsession.h b/third_party/QtWebApp/httpserver/httpsession.h index 2ddb9b94..35ed1f26 100644 --- a/third_party/QtWebApp/httpserver/httpsession.h +++ b/third_party/QtWebApp/httpserver/httpsession.h @@ -11,6 +11,8 @@ #include #include "httpglobal.h" +namespace stefanfrings { + /** This class stores data for a single HTTP session. A session can store any number of key/value pairs. This class uses implicit @@ -27,7 +29,7 @@ public: @param canStore The session can store data, if this parameter is true. Otherwise all calls to set() and remove() do not have any effect. */ - HttpSession(bool canStore=false); + HttpSession(const bool canStore=false); /** Copy constructor. Creates another HttpSession object that shares the @@ -115,4 +117,6 @@ private: }; +} // end of namespace + #endif // HTTPSESSION_H diff --git a/third_party/QtWebApp/httpserver/httpsessionstore.cpp b/third_party/QtWebApp/httpserver/httpsessionstore.cpp index 04f9cd04..6703f753 100644 --- a/third_party/QtWebApp/httpserver/httpsessionstore.cpp +++ b/third_party/QtWebApp/httpserver/httpsessionstore.cpp @@ -7,7 +7,9 @@ #include #include -HttpSessionStore::HttpSessionStore(QSettings* settings, QObject* parent) +using namespace stefanfrings; + +HttpSessionStore::HttpSessionStore(const QSettings *settings, QObject* parent) :QObject(parent) { this->settings=settings; diff --git a/third_party/QtWebApp/httpserver/httpsessionstore.h b/third_party/QtWebApp/httpserver/httpsessionstore.h index f3e11ae5..de86ecd2 100644 --- a/third_party/QtWebApp/httpserver/httpsessionstore.h +++ b/third_party/QtWebApp/httpserver/httpsessionstore.h @@ -15,6 +15,8 @@ #include "httpresponse.h" #include "httprequest.h" +namespace stefanfrings { + /** Stores HTTP sessions and deletes them when they have expired. The following configuration settings are required in the config file: @@ -35,8 +37,17 @@ class DECLSPEC HttpSessionStore : public QObject { Q_DISABLE_COPY(HttpSessionStore) public: - /** Constructor. */ - HttpSessionStore(QSettings* settings, QObject* parent=nullptr); + /** + Constructor. + @param settings Configuration settings, usually stored in an INI file. Must not be 0. + Settings are read from the current group, so the caller must have called settings->beginGroup(). + Because the group must not change during runtime, it is recommended to provide a + separate QSettings instance that is not used by other parts of the program. + The HttpSessionStore does not take over ownership of the QSettings instance, so the + caller should destroy it during shutdown. + @param parent Parent object + */ + HttpSessionStore(const QSettings* settings, QObject* parent=nullptr); /** Destructor */ virtual ~HttpSessionStore(); @@ -62,7 +73,7 @@ public: @return If autoCreate is disabled, the function returns a null session if there is no session. @see HttpSession::isNull() */ - HttpSession getSession(HttpRequest& request, HttpResponse& response, bool allowCreate=true); + HttpSession getSession(HttpRequest& request, HttpResponse& response, const bool allowCreate=true); /** Get a HTTP session by it's ID number. @@ -74,7 +85,7 @@ public: HttpSession getSession(const QByteArray id); /** Delete a session */ - void removeSession(HttpSession session); + void removeSession(const HttpSession session); protected: /** Storage for the sessions */ @@ -83,7 +94,7 @@ protected: private: /** Configuration settings */ - QSettings* settings; + const QSettings* settings; /** Timer to remove expired sessions */ QTimer cleanupTimer; @@ -103,4 +114,6 @@ private slots: void sessionTimerEvent(); }; +} // end of namespace + #endif // HTTPSESSIONSTORE_H diff --git a/third_party/QtWebApp/httpserver/staticfilecontroller.cpp b/third_party/QtWebApp/httpserver/staticfilecontroller.cpp index 679f5bae..9510aec9 100644 --- a/third_party/QtWebApp/httpserver/staticfilecontroller.cpp +++ b/third_party/QtWebApp/httpserver/staticfilecontroller.cpp @@ -8,13 +8,9 @@ #include #include -//YACReader----- -#include "httpsession.h" -#include "yacreader_http_session.h" -#include "static.h" -//-- +using namespace stefanfrings; -StaticFileController::StaticFileController(QSettings* settings, QObject* parent) +StaticFileController::StaticFileController(const QSettings *settings, QObject* parent) :HttpRequestHandler(parent) { maxAge=settings->value("maxAge","60000").toInt(); @@ -41,7 +37,7 @@ StaticFileController::StaticFileController(QSettings* settings, QObject* parent) } -void StaticFileController::service(HttpRequest& request, HttpResponse& response) +void StaticFileController::service(HttpRequest &request, HttpResponse &response) { QByteArray path=request.getPath(); // Check if we have the file in cache @@ -61,32 +57,6 @@ void StaticFileController::service(HttpRequest& request, HttpResponse& response) else { mutex.unlock(); - - //TODO(DONE) carga sensible al dispositivo y a la localización - QString stringPath = path; - QStringList paths = QString(path).split('/'); - QString fileName = paths.last(); - stringPath.remove(fileName); - HttpSession session=Static::sessionStore->getSession(request,response,false); - YACReaderHttpSession *ySession = Static::yacreaderSessionStore->getYACReaderSessionHttpSession(session.getId()); - QString device = "ipad"; - QString display = "@2x"; - if (ySession != nullptr) { - device = ySession->getDeviceType(); - display = ySession->getDisplayType(); - } - - if(fileName.endsWith(".png")) - fileName = getDeviceAwareFileName(fileName, device, display, request.getHeader("Accept-Language"), stringPath); - else - fileName = getDeviceAwareFileName(fileName, device, request.getHeader("Accept-Language"), stringPath); - QString newPath = stringPath.append(fileName); - path = newPath.toLocal8Bit(); - - //CAMBIADO - //response.setHeader("Connection","close"); - //END_TODO - // The file is not in cache. qDebug("StaticFileController: Cache miss for %s",path.data()); // Forbid access to files outside the docroot directory @@ -151,7 +121,7 @@ void StaticFileController::service(HttpRequest& request, HttpResponse& response) } } -void StaticFileController::setContentType(QString fileName, HttpResponse& response) const +void StaticFileController::setContentType(const QString fileName, HttpResponse &response) const { if (fileName.endsWith(".png")) { @@ -209,85 +179,17 @@ void StaticFileController::setContentType(QString fileName, HttpResponse& respon { response.setHeader("Content-Type", "application/font-otf"); } + else if (fileName.endsWith(".json")) + { + response.setHeader("Content-Type", "application/json"); + } + else if (fileName.endsWith(".xml")) + { + response.setHeader("Content-Type", "text/xml"); + } // Todo: add all of your content types else { qDebug("StaticFileController: unknown MIME type for filename '%s'", qPrintable(fileName)); } } - -//YACReader------------------------------------------------------------------------ - -bool StaticFileController::exists(QString localizedName, QString path) const -{ - QString fileName=docroot+"/"+path + localizedName; - QFile file(fileName); - return file.exists(); -} - -//retorna fileName si no se encontró alternativa traducida ó fileName-locale.extensión si se encontró -QString StaticFileController::getLocalizedFileName(QString fileName, QString locales, QString path) const -{ - QSet tried; // used to suppress duplicate attempts - QStringList locs=locales.split(',',QString::SkipEmptyParts); - QStringList fileNameParts = fileName.split('.'); - QString file = fileNameParts.first(); - QString extension = fileNameParts.last(); - // Search for exact match - foreach (QString loc,locs) { - loc.replace(QRegExp(";.*"),""); - loc.replace('-','_'); - QString localizedName=file+"-"+loc.trimmed()+"."+extension; - if (!tried.contains(localizedName)) { - if(exists(localizedName, path)) - return localizedName; - tried.insert(localizedName); - } - } - - // Search for correct language but any country - foreach (QString loc,locs) { - loc.replace(QRegExp("[;_-].*"),""); - QString localizedName=file+"-"+loc.trimmed()+"."+extension; - if (!tried.contains(localizedName)) { - if(exists(localizedName, path)) - return localizedName; - tried.insert(localizedName); - } - } - - return fileName; -} - -QString StaticFileController::getDeviceAwareFileName(QString fileName, QString device, QString locales, QString path) const -{ - QFileInfo fi(fileName); - QString baseName = fi.baseName(); - QString extension = fi.completeSuffix(); - - QString completeFileName = getLocalizedFileName(baseName+"_"+device+"."+extension,locales,path); - - if(QFile(docroot+"/"+path+completeFileName).exists()) - return completeFileName; //existe un archivo específico para este dispositivo y locales - else - return getLocalizedFileName(fileName,locales,path); //no hay archivo específico para el dispositivo, pero puede haberlo para estas locales -} - -QString StaticFileController::getDeviceAwareFileName(QString fileName, QString device, QString display, QString /* locales */, QString path) const -{ - QFileInfo fi(fileName); - QString baseName = fi.baseName(); - QString extension = fi.completeSuffix(); - - QString completeFileName = baseName+display+"."+extension; - if(QFile(docroot+"/"+path+completeFileName).exists()) - return completeFileName; - else - { - completeFileName = baseName+"_"+device+display+"."+extension; - if((QFile(docroot+"/"+path+completeFileName).exists())) - return completeFileName; - } - - return fileName; -} diff --git a/third_party/QtWebApp/httpserver/staticfilecontroller.h b/third_party/QtWebApp/httpserver/staticfilecontroller.h index bda713d8..8e61a9e0 100644 --- a/third_party/QtWebApp/httpserver/staticfilecontroller.h +++ b/third_party/QtWebApp/httpserver/staticfilecontroller.h @@ -13,6 +13,8 @@ #include "httpresponse.h" #include "httprequesthandler.h" +namespace stefanfrings { + /** Delivers static files. It is usually called by the applications main request handler when the caller requests a path that is mapped to static files. @@ -45,11 +47,20 @@ class DECLSPEC StaticFileController : public HttpRequestHandler { Q_DISABLE_COPY(StaticFileController) public: - /** Constructor */ - StaticFileController(QSettings* settings, QObject* parent = nullptr); + /** + Constructor. + @param settings Configuration settings, usually stored in an INI file. Must not be 0. + Settings are read from the current group, so the caller must have called settings->beginGroup(). + Because the group must not change during runtime, it is recommended to provide a + separate QSettings instance that is not used by other parts of the program. + The StaticFileController does not take over ownership of the QSettings instance, so the + caller should destroy it during shutdown. + @param parent Parent object + */ + StaticFileController(const QSettings* settings, QObject* parent = nullptr); /** Generates the response */ - void service(HttpRequest& request, HttpResponse& response) override; + void service(HttpRequest& request, HttpResponse& response); private: @@ -81,14 +92,9 @@ private: QMutex mutex; /** Set a content-type header in the response depending on the ending of the filename */ - void setContentType(QString file, HttpResponse& response) const; - - //YACReader------------------------------------------------------------------------ - QString getLocalizedFileName(QString fileName, QString locales, QString path) const; - QString getDeviceAwareFileName(QString fileName, QString device, QString locales, QString path) const; - QString getDeviceAwareFileName(QString fileName, QString device, QString display, QString locales, QString path) const; - - bool exists(QString localizedName, QString path) const; + void setContentType(const QString file, HttpResponse &response) const; }; +} // end of namespace + #endif // STATICFILECONTROLLER_H diff --git a/third_party/QtWebApp/templateengine/template.cpp b/third_party/QtWebApp/templateengine/template.cpp index 37941c99..a150dd80 100644 --- a/third_party/QtWebApp/templateengine/template.cpp +++ b/third_party/QtWebApp/templateengine/template.cpp @@ -6,14 +6,16 @@ #include "template.h" #include -Template::Template(QString source, QString sourceName) +using namespace stefanfrings; + +Template::Template(const QString source, const QString sourceName) : QString(source) { this->sourceName=sourceName; this->warnings=false; } -Template::Template(QFile& file, QTextCodec* textCodec) +Template::Template(QFile& file, const QTextCodec* textCodec) { this->warnings=false; sourceName=QFileInfo(file.fileName()).baseName(); @@ -34,7 +36,7 @@ Template::Template(QFile& file, QTextCodec* textCodec) } -int Template::setVariable(QString name, QString value) +int Template::setVariable(const QString name, const QString value) { int count=0; QString variable="{"+name+"}"; @@ -52,7 +54,7 @@ int Template::setVariable(QString name, QString value) return count; } -int Template::setCondition(QString name, bool value) +int Template::setCondition(const QString name, const bool value) { int count=0; QString startTag=QString("{if %1}").arg(name); @@ -150,7 +152,7 @@ int Template::setCondition(QString name, bool value) return count; } -int Template::loop(QString name, int repetitions) +int Template::loop(const QString name, const int repetitions) { Q_ASSERT(repetitions>=0); int count=0; @@ -234,7 +236,7 @@ int Template::loop(QString name, int repetitions) return count; } -void Template::enableWarnings(bool enable) +void Template::enableWarnings(const bool enable) { warnings=enable; } diff --git a/third_party/QtWebApp/templateengine/template.h b/third_party/QtWebApp/templateengine/template.h index d2782d9c..01716c53 100644 --- a/third_party/QtWebApp/templateengine/template.h +++ b/third_party/QtWebApp/templateengine/template.h @@ -14,6 +14,8 @@ #include #include "templateglobal.h" +namespace stefanfrings { + /** Enhanced version of QString for template processing. Templates are usually loaded from files, but may also be loaded from @@ -41,10 +43,10 @@ t.setVariable("username", "Stefan"); t.setCondition("locked",false); t.loop("user",2); - t.setVariable("user0.name,"Markus"); - t.setVariable("user0.time,"8:30"); - t.setVariable("user1.name,"Roland"); - t.setVariable("user1.time,"8:45"); + t.setVariable("user0.name","Markus"); + t.setVariable("user0.time","8:30"); + t.setVariable("user1.name","Roland"); + t.setVariable("user1.time","8:45");

The code example above shows how variable within loops are numbered. @@ -95,7 +97,7 @@ public: @param source The template source text @param sourceName Name of the source file, used for logging */ - Template(QString source, QString sourceName); + Template(const QString source, const QString sourceName); /** Constructor that reads the template from a file. Note that this class does not @@ -106,7 +108,7 @@ public: @see TemplateLoader @see TemplateCache */ - Template(QFile& file, QTextCodec* textCodec); + Template(QFile &file, const QTextCodec* textCodec); /** Replace a variable by the given value. @@ -121,7 +123,7 @@ public: @param value new value @return The count of variables that have been processed */ - int setVariable(QString name, QString value); + int setVariable(const QString name, const QString value); /** Set a condition. This affects tags with the syntax @@ -135,7 +137,7 @@ public: @param value Value of the condition @return The count of conditions that have been processed */ - int setCondition(QString name, bool value); + int setCondition(const QString name, bool value); /** Set number of repetitions of a loop. @@ -148,13 +150,13 @@ public: @param repetitions The number of repetitions @return The number of loops that have been processed */ - int loop(QString name, int repetitions); + int loop(QString name, const int repetitions); /** Enable warnings for missing tags @param enable Warnings are enabled, if true */ - void enableWarnings(bool enable=true); + void enableWarnings(const bool enable=true); private: @@ -165,4 +167,6 @@ private: bool warnings; }; +} // end of namespace + #endif // TEMPLATE_H diff --git a/third_party/QtWebApp/templateengine/templatecache.cpp b/third_party/QtWebApp/templateengine/templatecache.cpp index 06b1a0a7..e0f22bf5 100644 --- a/third_party/QtWebApp/templateengine/templatecache.cpp +++ b/third_party/QtWebApp/templateengine/templatecache.cpp @@ -3,7 +3,9 @@ #include #include -TemplateCache::TemplateCache(QSettings* settings, QObject* parent) +using namespace stefanfrings; + +TemplateCache::TemplateCache(const QSettings* settings, QObject* parent) :TemplateLoader(settings,parent) { cache.setMaxCost(settings->value("cacheSize","1000000").toInt()); @@ -11,14 +13,16 @@ TemplateCache::TemplateCache(QSettings* settings, QObject* parent) qDebug("TemplateCache: timeout=%i, size=%i",cacheTimeout,cache.maxCost()); } -QString TemplateCache::tryFile(QString localizedName) +QString TemplateCache::tryFile(const QString localizedName) { qint64 now=QDateTime::currentMSecsSinceEpoch(); + mutex.lock(); // search in cache qDebug("TemplateCache: trying cached %s",qPrintable(localizedName)); CacheEntry* entry=cache.object(localizedName); if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout)) { + mutex.unlock(); return entry->document; } // search on filesystem @@ -27,6 +31,7 @@ QString TemplateCache::tryFile(QString localizedName) entry->document=TemplateLoader::tryFile(localizedName); // Store in cache even when the file did not exist, to remember that there is no such file cache.insert(localizedName,entry,entry->document.size()); + mutex.unlock(); return entry->document; } diff --git a/third_party/QtWebApp/templateengine/templatecache.h b/third_party/QtWebApp/templateengine/templatecache.h index c9d95c1c..9251254b 100644 --- a/third_party/QtWebApp/templateengine/templatecache.h +++ b/third_party/QtWebApp/templateengine/templatecache.h @@ -5,6 +5,8 @@ #include "templateglobal.h" #include "templateloader.h" +namespace stefanfrings { + /** Caching template loader, reduces the amount of I/O and improves performance on remote file systems. The cache has a limited size, it prefers to keep @@ -46,10 +48,15 @@ public: /** Constructor. - @param settings configurations settings + @param settings Configuration settings, usually stored in an INI file. Must not be 0. + Settings are read from the current group, so the caller must have called settings->beginGroup(). + Because the group must not change during runtime, it is recommended to provide a + separate QSettings instance that is not used by other parts of the program. + The TemplateCache does not take over ownership of the QSettings instance, so the caller + should destroy it during shutdown. @param parent Parent object */ - TemplateCache(QSettings* settings, QObject* parent=nullptr); + TemplateCache(const QSettings* settings, QObject* parent=nullptr); protected: @@ -58,7 +65,7 @@ protected: @param localizedName Name of the template with locale to find @return The template document, or empty string if not found */ - virtual QString tryFile(QString localizedName); + virtual QString tryFile(const QString localizedName); private: @@ -73,6 +80,10 @@ private: /** Cache storage */ QCache cache; + /** Used to synchronize threads */ + QMutex mutex; }; +} // end of namespace + #endif // TEMPLATECACHE_H diff --git a/third_party/QtWebApp/templateengine/templateloader.cpp b/third_party/QtWebApp/templateengine/templateloader.cpp index 91fdee02..2379755c 100644 --- a/third_party/QtWebApp/templateengine/templateloader.cpp +++ b/third_party/QtWebApp/templateengine/templateloader.cpp @@ -10,7 +10,9 @@ #include #include -TemplateLoader::TemplateLoader(QSettings* settings, QObject* parent) +using namespace stefanfrings; + +TemplateLoader::TemplateLoader(const QSettings *settings, QObject *parent) : QObject(parent) { templatePath=settings->value("path",".").toString(); @@ -64,7 +66,6 @@ QString TemplateLoader::tryFile(QString localizedName) Template TemplateLoader::getTemplate(QString templateName, QString locales) { - mutex.lock(); QSet tried; // used to suppress duplicate attempts QStringList locs=locales.split(',',QString::SkipEmptyParts); @@ -78,7 +79,6 @@ Template TemplateLoader::getTemplate(QString templateName, QString locales) { QString document=tryFile(localizedName); if (!document.isEmpty()) { - mutex.unlock(); return Template(document,localizedName); } tried.insert(localizedName); @@ -95,7 +95,6 @@ Template TemplateLoader::getTemplate(QString templateName, QString locales) QString document=tryFile(localizedName); if (!document.isEmpty()) { - mutex.unlock(); return Template(document,localizedName); } tried.insert(localizedName); @@ -106,11 +105,9 @@ Template TemplateLoader::getTemplate(QString templateName, QString locales) QString document=tryFile(templateName); if (!document.isEmpty()) { - mutex.unlock(); return Template(document,templateName); } qCritical("TemplateCache: cannot find template %s",qPrintable(templateName)); - mutex.unlock(); return Template("",templateName); } diff --git a/third_party/QtWebApp/templateengine/templateloader.h b/third_party/QtWebApp/templateengine/templateloader.h index 464ad1ea..dd41fa53 100644 --- a/third_party/QtWebApp/templateengine/templateloader.h +++ b/third_party/QtWebApp/templateengine/templateloader.h @@ -13,6 +13,8 @@ #include "templateglobal.h" #include "template.h" +namespace stefanfrings { + /** Loads localized versions of template files. If the caller requests a file with the name "index" and the suffix is ".tpl" and the requested locale is "de_DE, de, en-US", @@ -20,7 +22,7 @@ - index-de_DE.tpl - index-de.tpl - - index-en_US.tpl + - index-en_US.tpl - index-en.tpl - index.tpl @@ -45,7 +47,7 @@ public: @param settings configurations settings @param parent parent object */ - TemplateLoader(QSettings* settings, QObject* parent=nullptr); + TemplateLoader(const QSettings* settings, QObject* parent=nullptr); /** Destructor */ virtual ~TemplateLoader(); @@ -59,7 +61,7 @@ public: ignored. @return If the template cannot be loaded, an error message is logged and an empty template is returned. */ - Template getTemplate(QString templateName, QString locales=QString()); + Template getTemplate(const QString templateName, const QString locales=QString()); protected: @@ -68,7 +70,7 @@ protected: @param localizedName Name of the template with locale to find @return The template document, or empty string if not found */ - virtual QString tryFile(QString localizedName); + virtual QString tryFile(const QString localizedName); /** Directory where the templates are searched */ QString templatePath; @@ -78,9 +80,8 @@ protected: /** Codec for decoding the files */ QTextCodec* textCodec; - - /** Used to synchronize threads */ - QMutex mutex; }; +} // end of namespace + #endif // TEMPLATELOADER_H