Solved QNetworkReply and QNetworAccessManager don't emit `finished` signal
-
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 therequests
library with an incorrect token, then everything works: the answer has status code401
The only thing that I came up with is using
setTransferTimeout
and waiting for the request to abort after this timeoutHere 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)
-
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 problembut what's wrong with HTTP/2 is yet to be figured out
-
Hi and welcome to devnet,
Did you already check the traffic with Wireshark to see the answer you are getting back from the server ?
-
@xueqli in the url, change https to http and try again
-
@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:
-
@Ronel_qtmaster unfortunately, that didn't help
-
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 -
@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 ?
-
@SGaist hmm, I haven't encountered this issue for other services
At first I thought that the problem is withwww-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 inwww-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 -
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
-
@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.
-
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 problembut what's wrong with HTTP/2 is yet to be figured out
-
-
@xueqli Nice catch !
I would still recommend opening a ticket.
-
@SGaist yea, i did: https://bugreports.qt.io/browse/QTBUG-123891
(and turned out bothHTTP/2
andwww-authenticate
headers are required to reproduce this) -
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 :-)