diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.cpp b/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.cpp index 91fa3365..d3f74860 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.cpp +++ b/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.cpp @@ -16,7 +16,7 @@ HttpConnectionHandler::HttpConnectionHandler(QSettings* settings, HttpRequestHan this->settings=settings; this->requestHandler=requestHandler; currentRequest=0; - busy = false; // pdiener: it is not busy if it is new + busy = false; // execute signals in my own thread moveToThread(this); socket.moveToThread(this); @@ -60,13 +60,14 @@ void HttpConnectionHandler::handleConnection(int socketDescriptor) { // Start timer for read timeout int readTimeout=settings->value("readTimeout",10000).toInt(); readTimer.start(readTimeout); + // delete previous request + delete currentRequest; currentRequest=0; } bool HttpConnectionHandler::isBusy() { - //return socket.isOpen(); - return busy; // pdiener: changed this from socket readout to bool variable + return busy; } void HttpConnectionHandler::setBusy() { @@ -76,7 +77,10 @@ void HttpConnectionHandler::setBusy() { void HttpConnectionHandler::readTimeout() { qDebug("HttpConnectionHandler (%p): read timeout occured",this); - socket.write("HTTP/1.1 408 request timeout\r\nConnection: close\r\n\r\n408 request timeout\r\n"); + + //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.disconnectFromHost(); delete currentRequest; currentRequest=0; @@ -86,15 +90,13 @@ void HttpConnectionHandler::readTimeout() { void HttpConnectionHandler::disconnected() { qDebug("HttpConnectionHandler (%p): disconnected", this); socket.close(); - delete currentRequest; - currentRequest=0; readTimer.stop(); - busy = false; // pdiener: now we have finished + busy = false; } void HttpConnectionHandler::read() { #ifdef SUPERVERBOSE - qDebug("HttpConnectionHandler (%x): read input",(unsigned int) this); + qDebug("HttpConnectionHandler (%p): read input",this); #endif // Create new HttpRequest object if necessary diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.h b/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.h index 1b793daa..ac720819 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.h +++ b/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.h @@ -44,15 +44,12 @@ public: /** Destructor */ virtual ~HttpConnectionHandler(); - /** Returns true, if this handler is busy */ + /** Returns true, if this handler is in use. */ bool isBusy(); /** Mark this handler as busy */ void setBusy(); - /** This shows the busy-state from a very early time */ - bool busy; - private: /** Configuration settings */ @@ -70,6 +67,9 @@ private: /** Dispatches received requests to services */ HttpRequestHandler* requestHandler; + /** This shows the busy-state from a very early time */ + bool busy; + /** Executes the htreads own event loop */ void run(); diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.cpp b/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.cpp index 030803f4..3aa00a4d 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.cpp +++ b/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.cpp @@ -13,27 +13,33 @@ HttpConnectionHandlerPool::HttpConnectionHandlerPool(QSettings* settings, HttpRe HttpConnectionHandlerPool::~HttpConnectionHandlerPool() { foreach(HttpConnectionHandler* handler, pool) { - delete handler; + connect(handler,SIGNAL(finished()),handler,SLOT(deleteLater())); + handler->quit(); } } -HttpConnectionHandler* HttpConnectionHandlerPool::getConnectionHandler() { +HttpConnectionHandler* HttpConnectionHandlerPool::getConnectionHandler() { HttpConnectionHandler* freeHandler=0; + mutex.lock(); + // find a free handler in pool foreach(HttpConnectionHandler* handler, pool) { if (!handler->isBusy()) { freeHandler=handler; + freeHandler->setBusy(); + break; } } + // create a new handler, if necessary if (!freeHandler) { - //CAMBIADO - int maxConnectionHandlers= 100;//settings->value("maxThreads",10).toInt(); + int maxConnectionHandlers=settings->value("maxThreads",100).toInt(); if (pool.count()setBusy(); pool.append(freeHandler); } } - if (freeHandler) freeHandler->busy = true; // pdiener: set it to busy-state immediately + mutex.unlock(); return freeHandler; } @@ -42,6 +48,7 @@ HttpConnectionHandler* HttpConnectionHandlerPool::getConnectionHandler() { void HttpConnectionHandlerPool::cleanup() { int maxIdleHandlers=settings->value("minThreads",1).toInt(); int idleCounter=0; + mutex.lock(); foreach(HttpConnectionHandler* handler, pool) { if (!handler->isBusy()) { if (++idleCounter > maxIdleHandlers) { @@ -53,4 +60,5 @@ void HttpConnectionHandlerPool::cleanup() { } } } + mutex.unlock(); } diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.h b/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.h index ec557d78..2dc94338 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.h +++ b/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandlerpool.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "httpconnectionhandler.h" /** @@ -13,14 +14,14 @@ Example for the required configuration settings:
   minThreads=1
-  maxThreads=10
+  maxThreads=100
   cleanupInterval=1000
   maxRequestSize=16000
   maxMultiPartSize=1000000
   
The pool is empty initially and grows with the number of concurrent connections. A timer removes one idle connection handler at each - interval, but a it leaves some spare handlers in memory to improve + interval, but it leaves some spare handlers in memory to improve performance. @see HttpConnectionHandler for description of config settings readTimeout @see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize @@ -59,6 +60,9 @@ private: /** Timer to clean-up unused connection handler */ QTimer cleanupTimer; + /** Used to synchronize threads */ + QMutex mutex; + private slots: /** Received from the clean-up timer. */ diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httplistener.cpp b/YACReaderLibrary/server/lib/bfHttpServer/httplistener.cpp index ad3eecee..bcac23cb 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httplistener.cpp +++ b/YACReaderLibrary/server/lib/bfHttpServer/httplistener.cpp @@ -50,7 +50,6 @@ void HttpListener::incomingConnection(int socketDescriptor) { QTcpSocket* socket=new QTcpSocket(this); socket->setSocketDescriptor(socketDescriptor); connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); - //CAMBIADO 503 por 429 socket->write("HTTP/1.1 503 too many connections\r\nConnection: close\r\n\r\nToo many connections\r\n"); socket->disconnectFromHost(); } diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httprequest.cpp b/YACReaderLibrary/server/lib/bfHttpServer/httprequest.cpp index f63b3502..520f48ea 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httprequest.cpp +++ b/YACReaderLibrary/server/lib/bfHttpServer/httprequest.cpp @@ -14,17 +14,6 @@ HttpRequest::HttpRequest(QSettings* settings) { expectedBodySize=0; maxSize=settings->value("maxRequestSize","16000").toInt(); maxMultiPartSize=settings->value("maxMultiPartSize","1000000").toInt(); - - // Convert relative path to absolute, based on the directory of the config file. -#ifdef Q_OS_WIN32 - if (QDir::isRelativePath(tempDir) && settings->format()!=QSettings::NativeFormat) -#else - if (QDir::isRelativePath(tempDir)) -#endif - { - QFileInfo configFile(settings->fileName()); - tempDir=QFileInfo(configFile.absolutePath(),tempDir).absoluteFilePath(); - } } void HttpRequest::readRequest(QTcpSocket& socket) { @@ -170,15 +159,22 @@ void HttpRequest::decodeRequestParams() { #ifdef SUPERVERBOSE qDebug("HttpRequest: extract and decode request parameters"); #endif + // Get URL parameters QByteArray rawParameters; - if (headers.value("Content-Type")=="application/x-www-form-urlencoded") { - rawParameters=bodyData; + int questionMark=path.indexOf('?'); + if (questionMark>=0) { + rawParameters=path.mid(questionMark+1); + path=path.left(questionMark); } - else { - int questionMark=path.indexOf('?'); - if (questionMark>=0) { - rawParameters=path.mid(questionMark+1); - path=path.left(questionMark); + // Get request body parameters + QByteArray contentType=headers.value("Content-Type"); + if (!bodyData.isEmpty() && (contentType.isEmpty() || contentType.startsWith("application/x-www-form-urlencoded"))) { + if (rawParameters.isEmpty()) { + rawParameters.append('&'); + rawParameters.append(bodyData); + } + else { + rawParameters=bodyData; } } // Split the parameters into pairs of value and name diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httprequest.h b/YACReaderLibrary/server/lib/bfHttpServer/httprequest.h index 943a62c5..e79fd112 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httprequest.h +++ b/YACReaderLibrary/server/lib/bfHttpServer/httprequest.h @@ -111,8 +111,9 @@ public: /** Decode an URL parameter. - E.g. replace "%23" by '#' and replace '+' by ' ' - @param source The url encoded string + E.g. replace "%23" by '#' and replace '+' by ' '. + @param source The url encoded strings + @see QUrl::toPercentEncoding for the reverse direction */ static QByteArray urlDecode(const QByteArray source); @@ -173,9 +174,6 @@ private: /** Maximum allowed size of multipart forms in bytes. */ int maxMultiPartSize; - /** Directory for temp files */ - QString tempDir; - /** Current size */ int currentSize; diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpresponse.cpp b/YACReaderLibrary/server/lib/bfHttpServer/httpresponse.cpp index 3c6ea03b..e6b3937b 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpresponse.cpp +++ b/YACReaderLibrary/server/lib/bfHttpServer/httpresponse.cpp @@ -109,7 +109,6 @@ void HttpResponse::writeText(QString text, bool lastPart) write(text.toAscii(),lastPart); } - bool HttpResponse::hasSentLastPart() const { return sentLastPart; } diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpsession.cpp b/YACReaderLibrary/server/lib/bfHttpServer/httpsession.cpp index 5c7abcbf..21716cdd 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpsession.cpp +++ b/YACReaderLibrary/server/lib/bfHttpServer/httpsession.cpp @@ -7,15 +7,14 @@ #include #include -#include "comic.h" HttpSession::HttpSession(bool canStore) { if (canStore) { dataPtr=new HttpSessionData(); - dataPtr->yacreaderSessionData.comic = 0; dataPtr->refCount=1; dataPtr->lastAccess=QDateTime::currentMSecsSinceEpoch(); dataPtr->id=QUuid::createUuid().toString().toAscii(); + dataPtr->yacreaderSessionData.comic = 0; #ifdef SUPERVERBOSE qDebug("HttpSession: created new session data with id %s",dataPtr->id.data()); #endif @@ -159,7 +158,6 @@ void HttpSession::setLastAccess() { } } - //AÑADIDO bool HttpSession::isComicOnDevice(const QString & hash) { diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpsession.h b/YACReaderLibrary/server/lib/bfHttpServer/httpsession.h index 6fce74f7..d013da51 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpsession.h +++ b/YACReaderLibrary/server/lib/bfHttpServer/httpsession.h @@ -13,6 +13,7 @@ #include #include class Comic; + /** This class stores data for a single HTTP session. A session can store any number of key/value pairs. This class uses implicit @@ -114,26 +115,26 @@ private: Comic * comic; }; - struct HttpSessionData { + struct HttpSessionData { - /** Unique ID */ - QByteArray id; + /** Unique ID */ + QByteArray id; - /** Timestamp of last access, set by the HttpSessionStore */ - qint64 lastAccess; + /** Timestamp of last access, set by the HttpSessionStore */ + qint64 lastAccess; - /** Reference counter */ - int refCount; + /** Reference counter */ + int refCount; - /** Used to synchronize threads */ - QReadWriteLock lock; + /** Used to synchronize threads */ + QReadWriteLock lock; - /** Storage for the key/value pairs; */ - QMap values; + /** Storage for the key/value pairs; */ + QMap values; YACReaderSessionData yacreaderSessionData; - }; + }; /** Pointer to the shared data. */ HttpSessionData* dataPtr; diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.h b/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.h index e68a2267..e65260d7 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.h +++ b/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.h @@ -31,7 +31,7 @@ class HttpSessionStore : public QObject { Q_OBJECT - Q_DISABLE_COPY(HttpSessionStore); + Q_DISABLE_COPY(HttpSessionStore) public: /** Constructor. */ diff --git a/YACReaderLibrary/server/lib/bfHttpServer/staticfilecontroller.cpp b/YACReaderLibrary/server/lib/bfHttpServer/staticfilecontroller.cpp index 670682ce..08293989 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/staticfilecontroller.cpp +++ b/YACReaderLibrary/server/lib/bfHttpServer/staticfilecontroller.cpp @@ -42,14 +42,18 @@ void StaticFileController::service(HttpRequest& request, HttpResponse& response) } // Check if we have the file in cache qint64 now=QDateTime::currentMSecsSinceEpoch(); + mutex.lock(); CacheEntry* entry=cache.object(path); - if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout)) { + if (entry && (cacheTimeout==0 || entry->created>now-cacheTimeout)) { + QByteArray document=entry->document; //copy the cached document, because other threads may destroy the cached entry immediately after mutex unlock. + mutex.unlock(); qDebug("StaticFileController: Cache hit for %s",path.data()); setContentType(path,response); response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000)); - response.write(entry->document); + response.write(document); } else { + mutex.unlock(); qDebug("StaticFileController: Cache miss for %s",path.data()); // The file is not in cache. // If the filename is a directory, append index.html. @@ -65,11 +69,11 @@ void StaticFileController::service(HttpRequest& request, HttpResponse& response) fileName = getLocalizedFileName(fileName, request.getHeader("Accept-Language"), stringPath); QString newPath = stringPath.append(fileName); //END_TODO - QFile file(docroot+newPath); + QFile file(docroot+path); if (file.exists()) { qDebug("StaticFileController: Open file %s",qPrintable(file.fileName())); if (file.open(QIODevice::ReadOnly)) { - setContentType(newPath,response); + setContentType(path,response); response.setHeader("Cache-Control","max-age="+QByteArray::number(maxAge/1000)); if (file.size()<=maxCachedFileSize) { // Return the file content and store it also in the cache @@ -80,7 +84,9 @@ void StaticFileController::service(HttpRequest& request, HttpResponse& response) entry->document.append(buffer); } entry->created=now; + mutex.lock(); cache.insert(request.getPath(),entry,entry->document.size()); + mutex.unlock(); } else { // Return the file content, do not store in cache @@ -113,22 +119,23 @@ void StaticFileController::setContentType(QString fileName, HttpResponse& respon else if (fileName.endsWith(".gif")) { response.setHeader("Content-Type", "image/gif"); } + else if (fileName.endsWith(".pdf")) { + response.setHeader("Content-Type", "application/pdf"); + } else if (fileName.endsWith(".txt")) { response.setHeader("Content-Type", qPrintable("text/plain; charset="+encoding)); } else if (fileName.endsWith(".html") || fileName.endsWith(".htm")) { - response.setHeader("Content-Type", qPrintable("text/html; charset=charset="+encoding)); + response.setHeader("Content-Type", qPrintable("text/html; charset="+encoding)); } - else if (fileName.endsWith(".js")) - response.setHeader("Content-Type", qPrintable("text/javascript; charset=charset="+encoding)); // Todo: add all of your content types } bool StaticFileController::exists(QString localizedName, QString path) const { - QString fileName=docroot+"/"+path + localizedName; - QFile file(fileName); - return file.exists(); + 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ó @@ -164,4 +171,3 @@ QString StaticFileController::getLocalizedFileName(QString fileName, QString loc return fileName; } - \ No newline at end of file diff --git a/YACReaderLibrary/server/lib/bfHttpServer/staticfilecontroller.h b/YACReaderLibrary/server/lib/bfHttpServer/staticfilecontroller.h index e671b1dc..9987378a 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/staticfilecontroller.h +++ b/YACReaderLibrary/server/lib/bfHttpServer/staticfilecontroller.h @@ -10,6 +10,7 @@ #include "httpresponse.h" #include "httprequesthandler.h" #include +#include /** Delivers static files. It is usually called by the applications main request handler when @@ -74,10 +75,13 @@ private: /** Cache storage */ QCache cache; + /** Used to synchronize cache access for threads */ + QMutex mutex; + /** Set a content-type header in the response depending on the ending of the filename */ void setContentType(QString file, HttpResponse& response) const; - QString getLocalizedFileName(QString fileName, QString locales, QString path) const; + QString StaticFileController::getLocalizedFileName(QString fileName, QString locales, QString path) const; bool exists(QString localizedName, QString path) const; }; diff --git a/YACReaderLibrary/server/lib/bfLogging/logmessage.cpp b/YACReaderLibrary/server/lib/bfLogging/logmessage.cpp index b5754e32..59e4d8da 100644 --- a/YACReaderLibrary/server/lib/bfLogging/logmessage.cpp +++ b/YACReaderLibrary/server/lib/bfLogging/logmessage.cpp @@ -49,7 +49,7 @@ QString LogMessage::toString(const QString& msgFormat, const QString& timestampF } QString threadId; - threadId.setNum((qint64)QThread::currentThreadId()); + threadId.setNum((unsigned int)QThread::currentThreadId()); decorated.replace("{thread}",threadId); // Fill in variables diff --git a/YACReaderLibrary/server/lib/bfTemplateEngine/template.cpp b/YACReaderLibrary/server/lib/bfTemplateEngine/template.cpp index 427f132e..daea4b51 100644 --- a/YACReaderLibrary/server/lib/bfTemplateEngine/template.cpp +++ b/YACReaderLibrary/server/lib/bfTemplateEngine/template.cpp @@ -33,7 +33,7 @@ int Template::setVariable(QString name, QString value) { while (start>=0) { replace(start, variable.length(), value); count++; - start=indexOf(variable,start+variable.length()); + start=indexOf(variable,start+value.length()); } if (count==0 && warnings) { qWarning("Template: missing variable %s in %s",qPrintable(variable),qPrintable(sourceName));