From 3bcadbfaf5b2aa4e9cb40c3d9c6b57fb86e46ffe Mon Sep 17 00:00:00 2001 From: Olivier Maridat Date: Fri, 5 Aug 2016 17:42:52 +0200 Subject: [PATCH] Try to fix #14 --- .gitignore | 1 + src/private/qhttpbase.hpp | 16 ++- src/private/qhttpclient_private.hpp | 7 +- src/private/qhttpserverconnection_private.hpp | 97 ++++++++++-------- src/qhttpclient.cpp | 2 +- src/qhttpserverconnection.cpp | 99 ++++++++++--------- 6 files changed, 121 insertions(+), 101 deletions(-) diff --git a/.gitignore b/.gitignore index 1f9d856..84100ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +build-ant # 3rdparty, tmp and build direcotry build.properties 3rdparty diff --git a/src/private/qhttpbase.hpp b/src/private/qhttpbase.hpp index 9f1a937..0af33d5 100644 --- a/src/private/qhttpbase.hpp +++ b/src/private/qhttpbase.hpp @@ -156,9 +156,11 @@ class HttpReader : public TBase public: void collectData(int atMost) { + icollectRequired = true; icollectCapacity = atMost; icollectedData.clear(); - icollectedData.reserve(atMost); + if ( atMost > 0 ) + icollectedData.reserve(atMost); } bool shouldCollect() const { @@ -166,10 +168,12 @@ class HttpReader : public TBase } bool append(const char* data, size_t length) { - int currentLength = icollectedData.length(); - - if ( (currentLength + (int)length) >= icollectCapacity ) - return false; // capacity if full + if ( !icollectRequired ) // not allowed to collect data + return false; + int newLength = icollectedData.length() + (int) length; + + if ( icollectCapacity > 0 && newLength > icollectCapacity ) + return false; // the capacity is full icollectedData.append(data, length); return true; @@ -179,6 +183,8 @@ class HttpReader : public TBase TReadState ireadState = EEmpty; bool isuccessful = false; + /// shall I collect incoming body data by myself? + bool icollectRequired = false; int icollectCapacity = 0; QByteArray icollectedData; }; diff --git a/src/private/qhttpclient_private.hpp b/src/private/qhttpclient_private.hpp index 68d8932..d6439ba 100644 --- a/src/private/qhttpclient_private.hpp +++ b/src/private/qhttpclient_private.hpp @@ -38,10 +38,9 @@ class QHttpClientPrivate : public HttpParser void release() { // if socket drops and http_parser can not call messageComplete, dispatch the ilastResponse - onDispatchResponse(); + finalizeConnection(); isocket.disconnectAllQtConnections(); - isocket.close(); isocket.release(); if ( ilastRequest ) { @@ -138,10 +137,10 @@ class QHttpClientPrivate : public HttpParser parse(buffer, readLength); } - onDispatchResponse(); + finalizeConnection(); } - void onDispatchResponse() { + void finalizeConnection() { // if ilastResponse has been sent previously, just return if ( ilastResponse->d_func()->ireadState == QHttpResponsePrivate::ESent ) return; diff --git a/src/private/qhttpserverconnection_private.hpp b/src/private/qhttpserverconnection_private.hpp index a2af5d7..277e937 100644 --- a/src/private/qhttpserverconnection_private.hpp +++ b/src/private/qhttpserverconnection_private.hpp @@ -26,51 +26,31 @@ namespace server { /////////////////////////////////////////////////////////////////////////////// class QHttpConnectionPrivate : public HttpParser { -protected: Q_DECLARE_PUBLIC(QHttpConnection) - QHttpConnection* const q_ptr; - -public: - QByteArray itempUrl; - - // Since there can only be one request/response pair per connection at any time even with pipelining. - QHttpRequest* ilastRequest = nullptr; - QHttpResponse* ilastResponse = nullptr; - - - TServerHandler ihandler = nullptr; - + public: explicit QHttpConnectionPrivate(QHttpConnection* q) : HttpParser(HTTP_REQUEST), q_ptr(q) { - - QObject::connect(q_func(), &QHttpConnection::disconnected, [this](){ - // if socket drops and http_parser can not call messageComplete, dispatch the ilastRequest - onDispatchRequest(); - isocket.release(); - - if ( ilastRequest ) - ilastRequest->deleteLater(); - if ( ilastResponse ) - ilastResponse->deleteLater(); - - q_func()->deleteLater(); - }); - + + QObject::connect( + q_func(), &QHttpConnection::disconnected, + [this](){ release(); } + ); + QHTTP_LINE_DEEPLOG } - + virtual ~QHttpConnectionPrivate() { QHTTP_LINE_DEEPLOG } - + void createSocket(qintptr sokDesc, TBackend bend) { isocket.ibackendType = bend; - + if ( bend == ETcpSocket ) { QTcpSocket* sok = new QTcpSocket( q_func() ); isocket.itcpSocket = sok; sok->setSocketDescriptor(sokDesc); - + QObject::connect(sok, &QTcpSocket::readyRead, [this](){ onReadyRead(); }); @@ -81,12 +61,12 @@ class QHttpConnectionPrivate : public HttpParser QObject::connect(sok, &QTcpSocket::disconnected, q_func(), &QHttpConnection::disconnected, Qt::QueuedConnection); - + } else if ( bend == ELocalSocket ) { QLocalSocket* sok = new QLocalSocket( q_func() ); isocket.ilocalSocket = sok; sok->setSocketDescriptor(sokDesc); - + QObject::connect(sok, &QLocalSocket::readyRead, [this](){ onReadyRead(); }); @@ -98,30 +78,63 @@ class QHttpConnectionPrivate : public HttpParser q_func(), &QHttpConnection::disconnected, Qt::QueuedConnection); } - + } - + + void release() { + // if socket drops and http_parser can not call + // messageComplete, dispatch the ilastRequest + onDispatchRequest(); + + isocket.disconnectAllQtConnections(); + isocket.release(); + + if ( ilastRequest ) { + ilastRequest->deleteLater(); + ilastRequest = nullptr; + } + + if ( ilastResponse ) { + ilastResponse->deleteLater(); + ilastResponse = nullptr; + } + + q_func()->deleteLater(); + } + +protected: + QHttpConnection* const q_ptr; + + QByteArray itempUrl; + + // Since there can only be one request/response pair per connection at any + // time even with pipelining. + QHttpRequest* ilastRequest = nullptr; + QHttpResponse* ilastResponse = nullptr; + + TServerHandler ihandler = nullptr; + public: void onReadyRead() { while ( isocket.bytesAvailable() > 0 ) { char buffer[4097] = {0}; size_t readLength = (size_t) isocket.readRaw(buffer, 4096); - + parse(buffer, readLength); } - + onDispatchRequest(); } - + void onDispatchRequest() { // if ilastRequest has been sent previously, just return if ( ilastRequest->d_func()->ireadState == QHttpRequestPrivate::ESent ) return; - + ilastRequest->d_func()->ireadState = QHttpRequestPrivate::ESent; emit ilastRequest->end(); } - + public: int messageBegin(http_parser* parser); int url(http_parser* parser, const char* at, size_t length); @@ -133,12 +146,12 @@ class QHttpConnectionPrivate : public HttpParser int headersComplete(http_parser* parser); int body(http_parser* parser, const char* at, size_t length); int messageComplete(http_parser* parser); - + #ifdef USE_CUSTOM_URL_CREATOR public: static QUrl createUrl(const char *urlData, const http_parser_url &urlInfo); #endif // USE_CUSTOM_URL_CREATOR - + }; /////////////////////////////////////////////////////////////////////////////// diff --git a/src/qhttpclient.cpp b/src/qhttpclient.cpp index f3eddc3..4b27e3c 100644 --- a/src/qhttpclient.cpp +++ b/src/qhttpclient.cpp @@ -231,7 +231,7 @@ QHttpClientPrivate::body(http_parser*, const char* at, size_t length) { if ( ilastResponse->d_func()->shouldCollect() ) { if ( !ilastResponse->d_func()->append(at, length) ) - onDispatchResponse(); // forcefully dispatch the ilastResponse + finalizeConnection(); // forcefully dispatch the ilastResponse return 0; } diff --git a/src/qhttpserverconnection.cpp b/src/qhttpserverconnection.cpp index 15441ea..0be3820 100644 --- a/src/qhttpserverconnection.cpp +++ b/src/qhttpserverconnection.cpp @@ -75,10 +75,10 @@ int QHttpConnectionPrivate::messageBegin(http_parser*) { itempUrl.clear(); itempUrl.reserve(128); - + if ( ilastRequest ) ilastRequest->deleteLater(); - + ilastRequest = new QHttpRequest(q_func()); return 0; } @@ -86,7 +86,7 @@ QHttpConnectionPrivate::messageBegin(http_parser*) { int QHttpConnectionPrivate::url(http_parser*, const char* at, size_t length) { Q_ASSERT(ilastRequest); - + itempUrl.append(at, length); return 0; } @@ -94,10 +94,10 @@ QHttpConnectionPrivate::url(http_parser*, const char* at, size_t length) { int QHttpConnectionPrivate::headerField(http_parser*, const char* at, size_t length) { CHECK_FOR_DISCONNECTED - - // insert the header we parsed previously - // into the header map - if ( !itempHeaderField.isEmpty() && !itempHeaderValue.isEmpty() ) { + + // insert the header we parsed previously + // into the header map + if ( !itempHeaderField.isEmpty() && !itempHeaderValue.isEmpty() ) { // header names are always lower-cased ilastRequest->d_func()->iheaders.insert( itempHeaderField.toLower(), @@ -109,7 +109,7 @@ QHttpConnectionPrivate::headerField(http_parser*, const char* at, size_t length) itempHeaderField.clear(); itempHeaderValue.clear(); } - + itempHeaderField.append(at, length); return 0; } @@ -117,65 +117,65 @@ QHttpConnectionPrivate::headerField(http_parser*, const char* at, size_t length) int QHttpConnectionPrivate::headerValue(http_parser*, const char* at, size_t length) { CHECK_FOR_DISCONNECTED - - itempHeaderValue.append(at, length); + + itempHeaderValue.append(at, length); return 0; } int QHttpConnectionPrivate::headersComplete(http_parser* parser) { CHECK_FOR_DISCONNECTED - -#if defined(USE_CUSTOM_URL_CREATOR) - // get parsed url - struct http_parser_url urlInfo; - int r = http_parser_parse_url(itempUrl.constData(), - itempUrl.size(), - parser->method == HTTP_CONNECT, - &urlInfo); - Q_ASSERT(r == 0); - Q_UNUSED(r); - - ilastRequest->d_func()->iurl = createUrl( - itempUrl.constData(), - urlInfo - ); + + #if defined(USE_CUSTOM_URL_CREATOR) + // get parsed url + struct http_parser_url urlInfo; + int r = http_parser_parse_url(itempUrl.constData(), + itempUrl.size(), + parser->method == HTTP_CONNECT, + &urlInfo); + Q_ASSERT(r == 0); + Q_UNUSED(r); + + ilastRequest->d_func()->iurl = createUrl( + itempUrl.constData(), + urlInfo + ); #else - ilastRequest->d_func()->iurl = QUrl(itempUrl); + ilastRequest->d_func()->iurl = QUrl(itempUrl); #endif // defined(USE_CUSTOM_URL_CREATOR) - + // set method ilastRequest->d_func()->imethod = static_cast(parser->method); - + // set version ilastRequest->d_func()->iversion = QString("%1.%2") .arg(parser->http_major) .arg(parser->http_minor); - + // Insert last remaining header ilastRequest->d_func()->iheaders.insert( itempHeaderField.toLower(), itempHeaderValue.toLower() ); - + // set client information if ( isocket.ibackendType == ETcpSocket ) { ilastRequest->d_func()->iremoteAddress = isocket.itcpSocket->peerAddress().toString(); ilastRequest->d_func()->iremotePort = isocket.itcpSocket->peerPort(); - + } else if ( isocket.ibackendType == ELocalSocket ) { ilastRequest->d_func()->iremoteAddress = isocket.ilocalSocket->fullServerName(); ilastRequest->d_func()->iremotePort = 0; // not used in local sockets } - + if ( ilastResponse ) ilastResponse->deleteLater(); ilastResponse = new QHttpResponse(q_func()); - + if ( parser->http_major < 1 || parser->http_minor < 1 ) ilastResponse->d_func()->ikeepAlive = false; - + // close the connection if response was the last packet QObject::connect(ilastResponse, &QHttpResponse::done, [this](bool wasTheLastPacket){ ikeepAlive = !wasTheLastPacket; @@ -184,29 +184,30 @@ QHttpConnectionPrivate::headersComplete(http_parser* parser) { isocket.close(); } }); - + // we are good to go! if ( ihandler ) ihandler(ilastRequest, ilastResponse); else emit q_ptr->newRequest(ilastRequest, ilastResponse); - + return 0; } int QHttpConnectionPrivate::body(http_parser*, const char* at, size_t length) { CHECK_FOR_DISCONNECTED - - ilastRequest->d_func()->ireadState = QHttpRequestPrivate::EPartial; - - if ( ilastRequest->d_func()->shouldCollect() ) { - if ( !ilastRequest->d_func()->append(at, length) ) - onDispatchRequest(); // forcefully dispatch the ilastRequest - + + ilastRequest->d_func()->ireadState = QHttpRequestPrivate::EPartial; + + if ( ilastRequest->d_func()->icollectRequired ) { + if ( !ilastRequest->d_func()->append(at, length) ) { + // forcefully dispatch the ilastRequest + onDispatchRequest(); + } return 0; } - + emit ilastRequest->data(QByteArray(at, length)); return 0; } @@ -214,9 +215,9 @@ QHttpConnectionPrivate::body(http_parser*, const char* at, size_t length) { int QHttpConnectionPrivate::messageComplete(http_parser*) { CHECK_FOR_DISCONNECTED - - // request is ready to be dispatched - ilastRequest->d_func()->isuccessful = true; + + // request is ready to be dispatched + ilastRequest->d_func()->isuccessful = true; ilastRequest->d_func()->ireadState = QHttpRequestPrivate::EComplete; emit q_ptr->completeRequest(ilastRequest, ilastResponse); return 0; @@ -253,10 +254,10 @@ QHttpConnectionPrivate::createUrl(const char *urlData, const http_parser_url &ur #endif url.setFragment(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_FRAGMENT)); url.setUserInfo(CHECK_AND_GET_FIELD(urlData, urlInfo, UF_USERINFO)); - + if (HAS_URL_FIELD(urlInfo, UF_PORT)) url.setPort(urlInfo.port); - + return url; }