Skip to content

Commit d0ff6c2

Browse files
handle redirect cookie association
1 parent 0b04847 commit d0ff6c2

2 files changed

Lines changed: 42 additions & 2 deletions

File tree

py/selenium/webdriver/common/api_request_context.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,22 @@ def _get_set_cookie_headers(resp: urllib3.BaseHTTPResponse) -> list[str]:
231231
return [sc] if sc else []
232232

233233

234+
def _resolve_redirect_url(resp: urllib3.BaseHTTPResponse, original_url: str) -> str:
235+
"""Return the final URL after any redirects.
236+
237+
urllib3's retry history records each hop. When redirects occurred,
238+
the last entry's redirect_location resolved against its URL gives
239+
the final destination. When no redirects occurred, the original
240+
request URL is returned unchanged.
241+
"""
242+
history = resp.retries.history if resp.retries else ()
243+
if history:
244+
last = history[-1]
245+
if last.url and last.redirect_location:
246+
return urllib.parse.urljoin(last.url, last.redirect_location)
247+
return original_url
248+
249+
234250
class _BaseRequestContext:
235251
"""Base class with shared HTTP request logic for API request contexts."""
236252

@@ -473,12 +489,16 @@ def _fetch(self, url: str, method: str, **kwargs: Any) -> APIResponse:
473489
url = self._append_params(url, kwargs)
474490
resp = self._execute_request(method, url, headers, body, kwargs)
475491

492+
# After redirects, associate cookies with the final destination's
493+
# origin, not the initial request URL.
494+
final_url = _resolve_redirect_url(resp, url)
495+
476496
# Process response cookies
477497
set_cookie_headers = _get_set_cookie_headers(resp)
478498
if set_cookie_headers:
479-
self._handle_response_cookies(set_cookie_headers, url)
499+
self._handle_response_cookies(set_cookie_headers, final_url)
480500

481-
response = self._build_response(resp, url)
501+
response = self._build_response(resp, final_url)
482502

483503
fail = kwargs.get("fail_on_status_code", self._fail_on_status_code)
484504
if fail and not response.ok:

py/test/unit/selenium/webdriver/common/api_request_context_tests.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ def do_GET(self):
9898
self.send_response(302)
9999
self.send_header("Location", "/ok")
100100
self.end_headers()
101+
elif path == "/redirect_with_cookies":
102+
self.send_response(302)
103+
self.send_header("Location", "/set_cookie?name=redirected&value=yes")
104+
self.end_headers()
101105
elif path == "/redirect_chain":
102106
import urllib.parse
103107

@@ -1474,6 +1478,22 @@ def test_e2e_redirect_chain_exceeds_limit(base_url):
14741478
ctx.dispose()
14751479

14761480

1481+
def test_e2e_redirect_cookies_associated_with_final_url(base_url):
1482+
"""Cookies from a redirected response must be associated with the final URL's origin."""
1483+
ctx = _IsolatedAPIRequestContext(base_url=base_url)
1484+
r = ctx.get("/redirect_with_cookies")
1485+
assert r.status == 200
1486+
assert r.text() == "cookie set"
1487+
# The response URL should be the final destination, not the redirect source
1488+
assert "/set_cookie" in r.url
1489+
assert "/redirect_with_cookies" not in r.url
1490+
# The cookie should be stored with the correct domain (from the final URL)
1491+
assert len(ctx._cookies) == 1
1492+
assert ctx._cookies[0]["name"] == "redirected"
1493+
assert ctx._cookies[0]["value"] == "yes"
1494+
ctx.dispose()
1495+
1496+
14771497
def test_e2e_cookie_set_and_sent(base_url):
14781498
ctx = _IsolatedAPIRequestContext(base_url=base_url)
14791499
# Server sets a cookie

0 commit comments

Comments
 (0)