fix(qbittorrent): recognize SID cookie from raw Set-Cookie header on login
qBittorrent (or its reverse proxy) was returning 204 No Content with an SID
cookie and no body on a successful login, but the client only treated the
response as success if body == "Ok." or the cookie was in the parsed
requests cookie jar (resp.cookies.get("SID")). In the reported case the
Set-Cookie header was present (set-cookie=yes) yet the jar was empty —
requests doesn't always populate the jar from such headers (proxy-set/oddly-
attributed cookies) — so a valid login was reported as "Unexpected response".
The user re-entering correct credentials never helped.
Detect a successful login from EITHER the parsed jar OR the raw Set-Cookie
header (cookie name == SID). qBittorrent only sets SID on a valid login, so
this remains authoritative. The diagnostic now lists the cookie names it saw
for future-proofing.
New regression test reproduces the exact 204 + Set-Cookie SID + empty jar
case. 387/387 backend tests pass; ruff clean.
This commit is contained in:
@@ -21,6 +21,8 @@ class QbittorrentClientTests(unittest.TestCase):
|
||||
resp.text = text
|
||||
resp.raise_for_status.return_value = None
|
||||
resp.status_code = 200
|
||||
resp.headers = {}
|
||||
resp.cookies = {}
|
||||
return resp
|
||||
|
||||
def _get_response(self, json_data: dict, status_code: int = 200) -> MagicMock:
|
||||
@@ -161,6 +163,24 @@ class QbittorrentClientTests(unittest.TestCase):
|
||||
|
||||
self.assertTrue(self.client._logged_in)
|
||||
|
||||
def test_login_accepts_sid_cookie_in_header_on_204(self) -> None:
|
||||
"""204 + Set-Cookie SID with no body (no jar entry) is a valid login.
|
||||
|
||||
Reproduces the reported case: qBittorrent (or its reverse proxy) returns
|
||||
204 No Content with an SID cookie, and requests doesn't always populate
|
||||
the cookie jar from such a header, so the cookie must be detected from
|
||||
the raw Set-Cookie header.
|
||||
"""
|
||||
resp = self._login_response("")
|
||||
resp.status_code = 204
|
||||
resp.cookies = {} # NOT in the parsed jar
|
||||
resp.headers = {"Set-Cookie": "SID=abc123; HttpOnly; path=/"}
|
||||
self.session.post.return_value = resp
|
||||
|
||||
self.client._login()
|
||||
|
||||
self.assertTrue(self.client._logged_in)
|
||||
|
||||
def test_login_empty_body_without_cookie_is_diagnostic(self) -> None:
|
||||
"""Empty 200 body with no SID surfaces a URL/proxy diagnostic hint."""
|
||||
resp = self._login_response("")
|
||||
|
||||
Reference in New Issue
Block a user