Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct
CookieJar and setHeader woes
Wallboy last edited by Wallboy
I have a PyQt5 application I'm developing that uses QNAM and different Cookie Jars depending on a few class specific variables that I check before making the request. I may make 5 different requests together in a loop. I originally was planning on using setCookieJar(cookieJar) just prior to making each request, however this does not seem to work as I'm getting the following error after the reply/s are finished:
"RuntimeError: wrapped C/C++ object of type QNetworkCookieJar has been deleted"
After inspection of the setCookieJar() source code in qnetworkaccessmanager.cpp (http://osxr.org:8080/qt/source/qtbase/src/network/access/qnetworkaccessmanager.cpp#0669), it seems everytime you call setCookieJar, Qt calls delete on whatever cookieJar you had set prior, hence why I would be getting the above error when QNAM tries to modify a cookie jar that no longer exists.
So I scrapped the idea of using QNAM's Cookie Jar handling and implemented my own handling of cookies for every request by using:
request.setAttribute(QNetworkRequest.CookieLoadControlAttribute, QNetworkRequest.Manual) request.setAttribute(QNetworkRequest.CookieSaveControlAttribute, QNetworkRequest.Manual)
These prevent automatically loading and saving of cookies in QNAM's cookieJar instance.
So to load the cookies before the request I tried doing something like the following:
cookiesToSend = self.cookieJars[account].cookiesForUrl(url) if cookiesToSend: request.setHeader(QNetworkRequest.CookieHeader, cookiesToSend)
However this doesn't seem to work! If I run the following line of code right after:
Returns None. Why? According to the docs, you are supposed to give it a list of QNetworkCookies (http://doc.qt.io/qt-5/qnetworkrequest.html#KnownHeaders-enum):
"Corresponds to the HTTP Cookie header and contains a QList<QNetworkCookie> representing the cookies to be sent back to the server."
Even in the C++ source code of createRequest() it calls something very similar (http://osxr.org:8080/qt/source/qtbase/src/network/access/qnetworkaccessmanager.cpp#1162):
QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url()); if (!cookies.isEmpty()) request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies));
Is there some quirk with PyQt5 that is preventing this from working because PyQt5 doesn't have QVariant.fromValue()? I then did a test with the following line of code:
cookiesToSend = self.cookieJars[account].cookiesForUrl(url) if cookiesToSend: for cookie in cookiesToSend: request.setHeader(QNetworkRequest.CookieHeader, cookie)
This works, but only the last cookie in the loop gets set. The others are overridden each loop. So the setHeader call here does accept single QNetworkCookies. (Just not a list of QNetworkCookie objects in PyQt...hmm)
At this point, I had to resort to the hacky way of using setRawHeader and currently do the following which does the job, but seems ugly:
cookiesToSend = self.cookieJars[account].cookiesForUrl(url) if cookiesToSend: cookieString = str() for cookie in cookiesToSend: cookieString += str(cookie.name(), 'utf-8')+'='+str(cookie.value(), 'utf-8')+'; ' cookieString = cookieString[:-2] # remove '; ' from last cookie request.setRawHeader(bytes('Cookie', 'utf-8'), bytes(cookieString, 'utf-8'))
In the docs of setRawHeader I also noticed the following:
"Note: Setting the same header twice overrides the previous setting. To accomplish the behaviour of multiple HTTP headers of the same name, you should concatenate the two values, separating them with a comma (",") and set one single raw header."
I'm guessing this is what they mean by what I did with setRawHeader()? Though I don't understand what they mean by using a comma.
Any tips/suggestions/answers to any of my above questions would be appreciated,