Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. QNetworkReply and QNetworAccessManager don't emit `finished` signal
Qt 6.11 is out! See what's new in the release blog

QNetworkReply and QNetworAccessManager don't emit `finished` signal

Scheduled Pinned Locked Moved Solved Qt for Python
14 Posts 3 Posters 4.0k Views 1 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.
  • X xueqli

    Hello,

    I am trying to access an API which requires authentication, but when I set incorrect bearer token in the header both reply and network manager don't emit finished signal.
    reply.isFinished() -> False
    reply.error() -> NoError
    reply.isRunning() -> True

    If I remove token, then everything's fine and request fails with an error ContentAccessDenied: 201
    If I set the correct token, then everything's working too
    If I use the requests library with an incorrect token, then everything works: the answer has status code 401

    The only thing that I came up with is using setTransferTimeout and waiting for the request to abort after this timeout

    Here is an example of what I'm trying to run:

    import sys
    
    from PyQt6.QtCore import QUrl
    from PyQt6.QtNetwork import QNetworkReply
    from PyQt6.QtNetwork import QNetworkRequest
    from PyQt6.QtNetwork import QNetworkAccessManager
    from PyQt6.QtWidgets import QApplication
    
    
    class ApiAccess:
        def __init__(self) -> None:
            self.manager = QNetworkAccessManager()
            self.manager.finished.connect(self.on_manager_finished)
    
            self.reply = None
            self.token = "123"
    
        def get(self, url: QUrl) -> None:
            request = QNetworkRequest(url)
            request.setRawHeader(b"Authorization", f"Bearer {self.token}".encode("utf-8"))
            request.setRawHeader(b"User-Agent", b"MyUserAgent")
    
            self.reply = self.manager.get(request)
            self.reply.finished.connect(self.on_reply_finished)
            self.reply.errorOccurred.connect(self.on_reply_error)
    
        def on_manager_finished(self, reply: QNetworkReply) -> None:
            print(reply)
    
        def on_reply_error(self, error: QNetworkReply.NetworkError) -> None:
            print(error)
    
        def on_reply_finished(self) -> None:
            data = self.reply.readAll().data()
            print(data)
    
    
    def main():
        app = QApplication(sys.argv)
    
        api = ApiAccess()
        url = QUrl("https://api.someresource.com/path?query")
        api.get(url)
    
        return app.exec()
    
    
    if __name__ == "__main__":
        raise SystemExit(main())
    

    Is this a bug, or am I missing something? (PyQt6 6.6.1)

    Ronel_qtmasterR Offline
    Ronel_qtmasterR Offline
    Ronel_qtmaster
    wrote on last edited by
    #3

    @xueqli in the url, change https to http and try again

    X 1 Reply Last reply
    0
    • SGaistS SGaist

      Hi and welcome to devnet,

      Did you already check the traffic with Wireshark to see the answer you are getting back from the server ?

      X Offline
      X Offline
      xueqli
      wrote on last edited by
      #4

      @SGaist Hi! What exactly am I supposed to look at in the traffic?
      It looks like the server does respond to the requests
      Here are some screens:
      qt_correct_token.png qt_wrong_token.png requests_correct_token.png requests_wrong_token.png

      1 Reply Last reply
      0
      • Ronel_qtmasterR Ronel_qtmaster

        @xueqli in the url, change https to http and try again

        X Offline
        X Offline
        xueqli
        wrote on last edited by
        #5

        @Ronel_qtmaster unfortunately, that didn't help

        1 Reply Last reply
        0
        • X Offline
          X Offline
          xueqli
          wrote on last edited by
          #6

          For now I'm pretty sure that this is related to QTBUG-122281, because when I connect the metaDataChanged signal and look into reply's headers, they contain information about the invalid token, but nothing happens after

          SGaistS 1 Reply Last reply
          0
          • X xueqli

            For now I'm pretty sure that this is related to QTBUG-122281, because when I connect the metaDataChanged signal and look into reply's headers, they contain information about the invalid token, but nothing happens after

            SGaistS Offline
            SGaistS Offline
            SGaist
            Lifetime Qt Champion
            wrote on last edited by
            #7

            @xueqli just to rule out the obvious, you have the same issue if you send requests to a different service ?

            Is there one that is accessible that you can share for testing purposes so we can test it ?

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

            X 1 Reply Last reply
            1
            • SGaistS SGaist

              @xueqli just to rule out the obvious, you have the same issue if you send requests to a different service ?

              Is there one that is accessible that you can share for testing purposes so we can test it ?

              X Offline
              X Offline
              xueqli
              wrote on last edited by
              #8

              @SGaist hmm, I haven't encountered this issue for other services
              At first I thought that the problem is with www-authenticate header field
              So I found the API which also sends it (https://registry-1.docker.io/v2/_catalog, for example), and it works fine (my API sends some other attributes in www-authenticate header, but they are all valid)

              The other difference I noticed, my API doesn't send any content (answer.content == b''), and all API I've tried so far send a non-empty error message, so maybe my problem lies here

              1 Reply Last reply
              0
              • X Offline
                X Offline
                xueqli
                wrote on last edited by
                #9

                i don't know, looks like it stuck somewhere in the QIODevice::read?

                (Pdb) self.reply.isOpen()
                True
                (Pdb) self.reply.bytesAvailable()
                0
                (Pdb) self.reply.read(1000)
                b''
                (Pdb) self.reply.read(1000)
                b''
                (Pdb) self.reply.read(1000)
                b''
                (Pdb) self.reply.read(1000)
                b''
                (Pdb) self.reply.read(1000)
                b''
                (Pdb) self.reply.close()
                got reply error! NetworkError.OperationCanceledError
                download progress! received=0 total=0
                manager finished!
                QIODevice::read (QNetworkReplyHttpImpl): device not open
                reply finished! b''
                

                and after i manually close reply it says, that device is not open and emits all the signals i want it to emit

                i also made up an http server, which sends similar headers and empty content and it magically works! (but with this "manual close" it has some different output:

                self.reply.isOpen()=True
                got reply error! NetworkError.OperationCanceledError
                download progress! received=0 total=0
                manager finished!
                QIODevice::read (QNetworkReplyHttpImpl): device not open
                reply finished! b''
                QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.
                

                i have no ideas. think i probably should give up and create workarounds

                SGaistS 1 Reply Last reply
                0
                • X xueqli

                  i don't know, looks like it stuck somewhere in the QIODevice::read?

                  (Pdb) self.reply.isOpen()
                  True
                  (Pdb) self.reply.bytesAvailable()
                  0
                  (Pdb) self.reply.read(1000)
                  b''
                  (Pdb) self.reply.read(1000)
                  b''
                  (Pdb) self.reply.read(1000)
                  b''
                  (Pdb) self.reply.read(1000)
                  b''
                  (Pdb) self.reply.read(1000)
                  b''
                  (Pdb) self.reply.close()
                  got reply error! NetworkError.OperationCanceledError
                  download progress! received=0 total=0
                  manager finished!
                  QIODevice::read (QNetworkReplyHttpImpl): device not open
                  reply finished! b''
                  

                  and after i manually close reply it says, that device is not open and emits all the signals i want it to emit

                  i also made up an http server, which sends similar headers and empty content and it magically works! (but with this "manual close" it has some different output:

                  self.reply.isOpen()=True
                  got reply error! NetworkError.OperationCanceledError
                  download progress! received=0 total=0
                  manager finished!
                  QIODevice::read (QNetworkReplyHttpImpl): device not open
                  reply finished! b''
                  QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.
                  

                  i have no ideas. think i probably should give up and create workarounds

                  SGaistS Offline
                  SGaistS Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on last edited by
                  #10

                  @xueqli before working around, please open a ticket on the bug report system with your findings. It seems you have a valid case to make.

                  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
                  1
                  • X Offline
                    X Offline
                    xueqli
                    wrote on last edited by xueqli
                    #11

                    i found out that apparently it has nothing to do with either contents or headers,
                    and the real reason is: the request was using HTTP/2 protocol
                    disabling HTTP/2 (request.setAttribute(request.Attribute.Http2AllowedAttribute, False)) solves my problem

                    but what's wrong with HTTP/2 is yet to be figured out

                    SGaistS 1 Reply Last reply
                    1
                    • X xueqli has marked this topic as solved on
                    • X xueqli

                      i found out that apparently it has nothing to do with either contents or headers,
                      and the real reason is: the request was using HTTP/2 protocol
                      disabling HTTP/2 (request.setAttribute(request.Attribute.Http2AllowedAttribute, False)) solves my problem

                      but what's wrong with HTTP/2 is yet to be figured out

                      SGaistS Offline
                      SGaistS Offline
                      SGaist
                      Lifetime Qt Champion
                      wrote on last edited by
                      #12

                      @xueqli Nice catch !

                      I would still recommend opening a ticket.

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

                      X 1 Reply Last reply
                      1
                      • SGaistS SGaist

                        @xueqli Nice catch !

                        I would still recommend opening a ticket.

                        X Offline
                        X Offline
                        xueqli
                        wrote on last edited by
                        #13

                        @SGaist yea, i did: https://bugreports.qt.io/browse/QTBUG-123891
                        (and turned out both HTTP/2 and www-authenticate headers are required to reproduce this)

                        1 Reply Last reply
                        1
                        • SGaistS Offline
                          SGaistS Offline
                          SGaist
                          Lifetime Qt Champion
                          wrote on last edited by
                          #14

                          Thanks for the link and thorough spelunking !

                          Since you have a Python reproducer, you should add it to the bug report, it will make things easier for developers to reproduce :-)

                          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
                          1

                          • Login

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