Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QNetworkAccessManager reusing connections -- breaks
Forum Updated to NodeBB v4.3 + New Features

QNetworkAccessManager reusing connections -- breaks

Scheduled Pinned Locked Moved Solved General and Desktop
18 Posts 7 Posters 2.0k Views 4 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • P Offline
    P Offline
    poncho524
    wrote on last edited by
    #1

    Why is QNetworkAccessManager pipelining when it is told not to?

    I'm trying to send a sequence of POST's, and chaining them based on the previous' QNetworkReply::finished().

    On QNetworkReply::finished(), i "deleteLater" the reply and issue the next POST. The next POST always fails because it is being pipelined and "deletelater" on the PREVIOUS reply closes the connection.

    I'm guessing this is a undocumented "feature".

    anyone have tips?

    artwawA 1 Reply Last reply
    0
    • C Offline
      C Offline
      ChrisW67
      wrote on last edited by ChrisW67
      #7

      @poncho524

      HTTP Pipelining is the sending of multiple HTTP/1.1 requests on the same connection without waiting for their corresponding responses. Your code explicitly waits for a response and does not send a second request until the first is finished (so QNAM cannot be pipelining behind your back). I think pipelining is irrelevant to your problem. You can check for the attribute QNetworkRequest::HttpPipeliningWasUsedAttribute on the reply to confirm this.

      QNetworkAccessManager will reuse connections for multiple requests to the same server/port or use multiple connections to the same server to process queued requests. Once again, you serialise requests, so the queue is at most one long and parallel execution is also a non-issue.

      Your real issue is that you delete the wrong reply here:

      void HttpClient::processFirstDone()
      {
        emit httpClientReady();
        m_reply->deleteLater();
      }
      

      The slot called when you emit that signal is executed immediately and overwrites m_reply. When the signal returns you then queue deletion of the new reply. Swap the two lines and things will probably improve.

      P JonBJ 2 Replies Last reply
      7
      • SGaistS Offline
        SGaistS Offline
        SGaist
        Lifetime Qt Champion
        wrote on last edited by
        #2

        Hi,

        Can you show the code you use ?

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply
        0
        • P poncho524

          Why is QNetworkAccessManager pipelining when it is told not to?

          I'm trying to send a sequence of POST's, and chaining them based on the previous' QNetworkReply::finished().

          On QNetworkReply::finished(), i "deleteLater" the reply and issue the next POST. The next POST always fails because it is being pipelined and "deletelater" on the PREVIOUS reply closes the connection.

          I'm guessing this is a undocumented "feature".

          anyone have tips?

          artwawA Offline
          artwawA Offline
          artwaw
          wrote on last edited by
          #3

          @poncho524 Please show your code, I never encountered similar problem?

          For more information please re-read.

          Kind Regards,
          Artur

          1 Reply Last reply
          0
          • P Offline
            P Offline
            poncho524
            wrote on last edited by
            #4

            Wireshark shows both POST's being sent via the same connection. it also shows the Qt app sending a FIN ACK before the 2nd POST is completed. this breaks the signals coming back from the 2nd POST... "processNextDone" and "processNextError" never get called.

            void HttpClient::somefunction()
            {
              qNam=new QNetworkAccessManager;
            
              QNetworkRequest req(QUrl("http://someip/somepath"));
              req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
            
              QJsonDocument doc;
              QJsonObject obj;
              obj.insert("somefield", "somevalue");
            
              doc.setObject(obj);
            
              m_reply=qNam->post(req, doc.toJson());
            
              connect(m_reply, &QNetworkReply::finished, this, &HttpClient::processFirstDone);
              
              connect(this, &HttpClient::httpClientReady, this, &HttpClient::sendNext);
            }
            
            
            void HttpClient::processFirstDone()
            {
              emit httpClientReady();
              m_reply->deleteLater();
            }
            
            
            void HttpClient::sendNext()
            {
              QNetworkRequest req(QUrl("http://someip/someotherpath"));
              req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
            
              QJsonDocument doc;
              QJsonObject obj;
              obj.insert("somefield", "somevalue");
            
              doc.setObject(obj);
            
              m_reply=qNam->post(req, doc.toJson());
            
              connect(m_reply, &QNetworkReply::finished, this, &HttpClient::processNextDone);
              connect(m_reply, &QNetworkReply::errorOccurred, this, &HttpClient::processNextError);
            }
            
            
            // this never gets called
            void HttpClient::processNextDone()
            {
              printf("processNextDone()\n");
            }
            
            // this never gets called
            void HttpClient::processNextError()
            {
              printf("processNextDone()\n");
            }
            
            P 1 Reply Last reply
            0
            • P poncho524

              Wireshark shows both POST's being sent via the same connection. it also shows the Qt app sending a FIN ACK before the 2nd POST is completed. this breaks the signals coming back from the 2nd POST... "processNextDone" and "processNextError" never get called.

              void HttpClient::somefunction()
              {
                qNam=new QNetworkAccessManager;
              
                QNetworkRequest req(QUrl("http://someip/somepath"));
                req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
              
                QJsonDocument doc;
                QJsonObject obj;
                obj.insert("somefield", "somevalue");
              
                doc.setObject(obj);
              
                m_reply=qNam->post(req, doc.toJson());
              
                connect(m_reply, &QNetworkReply::finished, this, &HttpClient::processFirstDone);
                
                connect(this, &HttpClient::httpClientReady, this, &HttpClient::sendNext);
              }
              
              
              void HttpClient::processFirstDone()
              {
                emit httpClientReady();
                m_reply->deleteLater();
              }
              
              
              void HttpClient::sendNext()
              {
                QNetworkRequest req(QUrl("http://someip/someotherpath"));
                req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
              
                QJsonDocument doc;
                QJsonObject obj;
                obj.insert("somefield", "somevalue");
              
                doc.setObject(obj);
              
                m_reply=qNam->post(req, doc.toJson());
              
                connect(m_reply, &QNetworkReply::finished, this, &HttpClient::processNextDone);
                connect(m_reply, &QNetworkReply::errorOccurred, this, &HttpClient::processNextError);
              }
              
              
              // this never gets called
              void HttpClient::processNextDone()
              {
                printf("processNextDone()\n");
              }
              
              // this never gets called
              void HttpClient::processNextError()
              {
                printf("processNextDone()\n");
              }
              
              P Offline
              P Offline
              poncho524
              wrote on last edited by
              #5

              this is with QT 5.15.2

              1 Reply Last reply
              0
              • P Offline
                P Offline
                poncho524
                wrote on last edited by
                #6

                https://doc.qt.io/qt-5/qnetworkrequest.html#Attribute-enum

                QNetworkRequest::HttpPipeliningAllowedAttribute
                Requests only, type: QMetaType::Bool (default: false) Indicates whether the QNetworkAccessManager code is allowed to use HTTP pipelining with this request.

                1 Reply Last reply
                0
                • C Offline
                  C Offline
                  ChrisW67
                  wrote on last edited by ChrisW67
                  #7

                  @poncho524

                  HTTP Pipelining is the sending of multiple HTTP/1.1 requests on the same connection without waiting for their corresponding responses. Your code explicitly waits for a response and does not send a second request until the first is finished (so QNAM cannot be pipelining behind your back). I think pipelining is irrelevant to your problem. You can check for the attribute QNetworkRequest::HttpPipeliningWasUsedAttribute on the reply to confirm this.

                  QNetworkAccessManager will reuse connections for multiple requests to the same server/port or use multiple connections to the same server to process queued requests. Once again, you serialise requests, so the queue is at most one long and parallel execution is also a non-issue.

                  Your real issue is that you delete the wrong reply here:

                  void HttpClient::processFirstDone()
                  {
                    emit httpClientReady();
                    m_reply->deleteLater();
                  }
                  

                  The slot called when you emit that signal is executed immediately and overwrites m_reply. When the signal returns you then queue deletion of the new reply. Swap the two lines and things will probably improve.

                  P JonBJ 2 Replies Last reply
                  7
                  • C ChrisW67

                    @poncho524

                    HTTP Pipelining is the sending of multiple HTTP/1.1 requests on the same connection without waiting for their corresponding responses. Your code explicitly waits for a response and does not send a second request until the first is finished (so QNAM cannot be pipelining behind your back). I think pipelining is irrelevant to your problem. You can check for the attribute QNetworkRequest::HttpPipeliningWasUsedAttribute on the reply to confirm this.

                    QNetworkAccessManager will reuse connections for multiple requests to the same server/port or use multiple connections to the same server to process queued requests. Once again, you serialise requests, so the queue is at most one long and parallel execution is also a non-issue.

                    Your real issue is that you delete the wrong reply here:

                    void HttpClient::processFirstDone()
                    {
                      emit httpClientReady();
                      m_reply->deleteLater();
                    }
                    

                    The slot called when you emit that signal is executed immediately and overwrites m_reply. When the signal returns you then queue deletion of the new reply. Swap the two lines and things will probably improve.

                    P Offline
                    P Offline
                    poncho524
                    wrote on last edited by
                    #8

                    @ChrisW67 , oh man, i feel like a jerk now. nice catch!

                    1 Reply Last reply
                    0
                    • C Offline
                      C Offline
                      ChrisW67
                      wrote on last edited by
                      #9

                      We've all been there (or we are not trying hard enough).

                      1 Reply Last reply
                      1
                      • C ChrisW67

                        @poncho524

                        HTTP Pipelining is the sending of multiple HTTP/1.1 requests on the same connection without waiting for their corresponding responses. Your code explicitly waits for a response and does not send a second request until the first is finished (so QNAM cannot be pipelining behind your back). I think pipelining is irrelevant to your problem. You can check for the attribute QNetworkRequest::HttpPipeliningWasUsedAttribute on the reply to confirm this.

                        QNetworkAccessManager will reuse connections for multiple requests to the same server/port or use multiple connections to the same server to process queued requests. Once again, you serialise requests, so the queue is at most one long and parallel execution is also a non-issue.

                        Your real issue is that you delete the wrong reply here:

                        void HttpClient::processFirstDone()
                        {
                          emit httpClientReady();
                          m_reply->deleteLater();
                        }
                        

                        The slot called when you emit that signal is executed immediately and overwrites m_reply. When the signal returns you then queue deletion of the new reply. Swap the two lines and things will probably improve.

                        JonBJ Offline
                        JonBJ Offline
                        JonB
                        wrote on last edited by
                        #10

                        @ChrisW67 said in QNetworkAccessManager reusing connections -- breaks:

                        Your real issue is that you delete the wrong reply here:
                        void HttpClient::processFirstDone()
                        {
                        emit httpClientReady();
                        m_reply->deleteLater();
                        }

                        The slot called when you emit that signal is executed immediately and overwrites m_reply. When the signal returns you then queue deletion of the new reply. Swap the two lines and things will probably improve.

                        Hi Chris, this is a question for you, as you know more than I.

                        It worries me when you say to put the deleteLater() first. The reply must remain valid till this slot exits and returns, right? And the delete will happen as soon as the main event loop gets a chance to do so. How do you know what emitting the httpClientReady signal might cause to happen in the outside world? It could cause the main loop to be reached and the delete to happen, yes or no?

                        So.... would the following pattern be "safer"?

                        void HttpClient::processFirstDone()
                        {
                          QNetworkReply *oldReply = m_reply;
                          emit httpClientReady();
                          oldReply->deleteLater();
                        }
                        
                        JKSHJ 1 Reply Last reply
                        0
                        • JonBJ JonB

                          @ChrisW67 said in QNetworkAccessManager reusing connections -- breaks:

                          Your real issue is that you delete the wrong reply here:
                          void HttpClient::processFirstDone()
                          {
                          emit httpClientReady();
                          m_reply->deleteLater();
                          }

                          The slot called when you emit that signal is executed immediately and overwrites m_reply. When the signal returns you then queue deletion of the new reply. Swap the two lines and things will probably improve.

                          Hi Chris, this is a question for you, as you know more than I.

                          It worries me when you say to put the deleteLater() first. The reply must remain valid till this slot exits and returns, right? And the delete will happen as soon as the main event loop gets a chance to do so. How do you know what emitting the httpClientReady signal might cause to happen in the outside world? It could cause the main loop to be reached and the delete to happen, yes or no?

                          So.... would the following pattern be "safer"?

                          void HttpClient::processFirstDone()
                          {
                            QNetworkReply *oldReply = m_reply;
                            emit httpClientReady();
                            oldReply->deleteLater();
                          }
                          
                          JKSHJ Offline
                          JKSHJ Offline
                          JKSH
                          Moderators
                          wrote on last edited by
                          #11

                          @JonB said in QNetworkAccessManager reusing connections -- breaks:

                          The reply must remain valid till this slot exits and returns, right?

                          Right.

                          And the delete will happen as soon as the main event loop gets a chance to do so.

                          I'm pretty sure the delete will happen after the main event loop finishes processing everything else in the event queue, not "as soon as it gets a chance".

                          It could cause the main loop to be reached and the delete to happen, yes or no?

                          No. After the event loop passed control to HttpClient::processFirstDone(), it cannot receive control again until HttpClient::processFirstDone() returns.

                          If emit httpClientReady() has a Qt::DirectConnection, the connected slot will be called from inside HttpClient::processFirstDone() (so the event loop is not involved). If emit httpClientReady() has a Qt::QueuedConnection, the connected slot can't run until after HttpClient::processFirstDone() returns.

                          Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                          JonBJ 1 Reply Last reply
                          1
                          • JKSHJ JKSH

                            @JonB said in QNetworkAccessManager reusing connections -- breaks:

                            The reply must remain valid till this slot exits and returns, right?

                            Right.

                            And the delete will happen as soon as the main event loop gets a chance to do so.

                            I'm pretty sure the delete will happen after the main event loop finishes processing everything else in the event queue, not "as soon as it gets a chance".

                            It could cause the main loop to be reached and the delete to happen, yes or no?

                            No. After the event loop passed control to HttpClient::processFirstDone(), it cannot receive control again until HttpClient::processFirstDone() returns.

                            If emit httpClientReady() has a Qt::DirectConnection, the connected slot will be called from inside HttpClient::processFirstDone() (so the event loop is not involved). If emit httpClientReady() has a Qt::QueuedConnection, the connected slot can't run until after HttpClient::processFirstDone() returns.

                            JonBJ Offline
                            JonBJ Offline
                            JonB
                            wrote on last edited by JonB
                            #12

                            @JKSH
                            The question is what could happen if a programmer puts a QApplication::ProcessEvents() in whatever this slot calls (whatever emitting httpClientReady() causes)? And we know how many users like to call this, with or without a local QEventLoop....

                            Now, it may be that this comes down to https://doc.qt.io/qt-5/qcoreapplication.html#processEvents and what it says about DeferredDelete events, which I believe is what deleteLater() calls/uses?

                            In the event that you are running a local loop which calls this function continuously, without an event loop, the DeferredDelete events will not be processed. This can affect the behaviour of widgets, e.g. QToolTip, that rely on DeferredDelete events to function properly. An alternative would be to call sendPostedEvents() from within that local loop.

                            So, please, I invite you to comment, because this is an area I have been unsure about. Are you saying Qt is saying that even with some QApplication::processEvents() somewhere Qt will not act on a pending deleteLater()? That is my area of concern.

                            1 Reply Last reply
                            0
                            • Christian EhrlicherC Offline
                              Christian EhrlicherC Offline
                              Christian Ehrlicher
                              Lifetime Qt Champion
                              wrote on last edited by
                              #13

                              QCoreApplication::processEvents() does not run the event loop, it just processes events so QObject::deleteLater() will not be called in there ('The object will be deleted when control returns to the event loop.') :)
                              Technically deleteLater is also only an event but it's handled specially like this comment in qcoreapplication.cpp show us:

                              // Events sent by non-Qt event handlers (such as glib) may not
                              // have the scopeLevel set correctly. The scope level makes sure that
                              // code like this:
                              //     foo->deleteLater();
                              //     qApp->processEvents(); // without passing QEvent::DeferredDelete
                              // will not cause "foo" to be deleted before returning to the event loop.
                              

                              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                              Visit the Qt Academy at https://academy.qt.io/catalog

                              JonBJ 1 Reply Last reply
                              4
                              • Christian EhrlicherC Christian Ehrlicher

                                QCoreApplication::processEvents() does not run the event loop, it just processes events so QObject::deleteLater() will not be called in there ('The object will be deleted when control returns to the event loop.') :)
                                Technically deleteLater is also only an event but it's handled specially like this comment in qcoreapplication.cpp show us:

                                // Events sent by non-Qt event handlers (such as glib) may not
                                // have the scopeLevel set correctly. The scope level makes sure that
                                // code like this:
                                //     foo->deleteLater();
                                //     qApp->processEvents(); // without passing QEvent::DeferredDelete
                                // will not cause "foo" to be deleted before returning to the event loop.
                                
                                JonBJ Offline
                                JonBJ Offline
                                JonB
                                wrote on last edited by
                                #14

                                @Christian-Ehrlicher
                                Thank you, helpful. So to be clear:

                                control returns to the event loop

                                • There is just one, unique main Qt event loop running? (Or maybe more precisely one per thread?)
                                • User cannot write code that, from a slot/anywhere, itself can enter that loop?
                                1 Reply Last reply
                                0
                                • Christian EhrlicherC Offline
                                  Christian EhrlicherC Offline
                                  Christian Ehrlicher
                                  Lifetime Qt Champion
                                  wrote on last edited by
                                  #15

                                  @JonB said in QNetworkAccessManager reusing connections -- breaks:

                                  User cannot write code that, from a slot/anywhere, itself can enter that loop?

                                  more or less - calling QCoreApplication::processEvent() executes the loop, but with a higher scopeLevel counter I would guess.

                                  Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                                  Visit the Qt Academy at https://academy.qt.io/catalog

                                  JonBJ 1 Reply Last reply
                                  1
                                  • Christian EhrlicherC Christian Ehrlicher

                                    @JonB said in QNetworkAccessManager reusing connections -- breaks:

                                    User cannot write code that, from a slot/anywhere, itself can enter that loop?

                                    more or less - calling QCoreApplication::processEvent() executes the loop, but with a higher scopeLevel counter I would guess.

                                    JonBJ Offline
                                    JonBJ Offline
                                    JonB
                                    wrote on last edited by
                                    #16

                                    @Christian-Ehrlicher said in QNetworkAccessManager reusing connections -- breaks:

                                    higher scopeLevel counter

                                    OK that makes sense, is your wording "conceptual" or does Qt code pass some actual counter around to indicate a level?

                                    1 Reply Last reply
                                    0
                                    • Christian EhrlicherC Offline
                                      Christian EhrlicherC Offline
                                      Christian Ehrlicher
                                      Lifetime Qt Champion
                                      wrote on last edited by
                                      #17

                                      @JonB said in QNetworkAccessManager reusing connections -- breaks:

                                      or does Qt code pass some actual counter around to indicate a level?

                                      There is a counter: https://code.woboq.org/qt5/qtbase/src/corelib/kernel/qcoreapplication.cpp.html#1574

                                      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                                      Visit the Qt Academy at https://academy.qt.io/catalog

                                      JonBJ 1 Reply Last reply
                                      2
                                      • Christian EhrlicherC Christian Ehrlicher

                                        @JonB said in QNetworkAccessManager reusing connections -- breaks:

                                        or does Qt code pass some actual counter around to indicate a level?

                                        There is a counter: https://code.woboq.org/qt5/qtbase/src/corelib/kernel/qcoreapplication.cpp.html#1574

                                        JonBJ Offline
                                        JonBJ Offline
                                        JonB
                                        wrote on last edited by
                                        #18

                                        @Christian-Ehrlicher Perfect, in context that code & comments all makes sense to me now.

                                        1 Reply Last reply
                                        0

                                        • Login

                                        • Login or register to search.
                                        • First post
                                          Last post
                                        0
                                        • Categories
                                        • Recent
                                        • Tags
                                        • Popular
                                        • Users
                                        • Groups
                                        • Search
                                        • Get Qt Extensions
                                        • Unsolved