Incorrectly strips trailing dot from absolute hostnames, causing TooManyRedirects
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 54.3k
- Forks
- 10.4k
- Avg merge
- 16h 43m
- Merged PRs (30d)
- 3
Description
requests appears to be incorrectly stripping the trailing dot on absolute hostnames (i.e. explicitly marked as in the root DNS namespace) in URLs when constructing the Host: request header, like pyropus.ca.. This causes redirect loops when the site chooses the absolute hostname as canonical and redirects requests without the trailing dot.
The example site redirects requests with a host header of Host: pyropus.ca to the same URL with the host name changed to the canonical absolute version:
HTTP/1.1 301 Moved Permanently
[...]
Location: https://pyropus.ca./
requests then strips the dot and resends the request, resulting in a redirect loop.
The trailing dot needs to be stripped from the SNI header for https requests (required by TLS/etc spec) but it should not be stripped from the Host: header value.
When fixing the SNI trailing dot issue, curl had this bug because they changed it to also affect the Host: header value, but they've fixed it by reverting that part of the change:
https://github.com/curl/curl/issues/8290
Many other common user agents that I've tested handle this properly:
- all GUI browers, to my knowledge, preserve the absolute domain name in the Host: header - I've tested Firefox, Chromium, Vivaldi, Konqueror, and Safari
- wget also preserves the absolute domain name on redirect URLs
- curl is now fixed and handles it
A few less common ones have the same buggy behaviour, or did -- I haven't re-checked:
- lynx, links, and elinks handle it like curl, erroring out on a redirect loop
Expected Result
Successful 200 response with content.
Actual Result
Traceback (most recent call last):
File "<string>", line 1, in <module>
File ".../test/.venv/lib/python3.9/site-packages/requests/api.py", line 75, in get
return request('get', url, params=params, **kwargs)
File ".../test/.venv/lib/python3.9/site-packages/requests/api.py", line 61, in request
return session.request(method=method, url=url, **kwargs)
File ".../test/.venv/lib/python3.9/site-packages/requests/sessions.py", line 529, in request
resp = self.send(prep, **send_kwargs)
File ".../test/.venv/lib/python3.9/site-packages/requests/sessions.py", line 667, in send
history = [resp for resp in gen]
File ".../test/.venv/lib/python3.9/site-packages/requests/sessions.py", line 667, in <listcomp>
history = [resp for resp in gen]
File ".../test/.venv/lib/python3.9/site-packages/requests/sessions.py", line 166, in resolve_redirects
raise TooManyRedirects('Exceeded {} redirects.'.format(self.max_redirects), response=resp)
requests.exceptions.TooManyRedirects: Exceeded 30 redirects.
Reproduction Steps
import requests
requests.get("https://pyropus.ca./")
System Information
$ python -m requests.help
{
"chardet": {
"version": null
},
"charset_normalizer": {
"version": "2.0.12"
},
"cryptography": {
"version": ""
},
"idna": {
"version": "3.3"
},
"implementation": {
"name": "CPython",
"version": "3.9.2"
},
"platform": {
"release": "5.17.5",
"system": "Linux"
},
"pyOpenSSL": {
"openssl_version": "",
"version": null
},
"requests": {
"version": "2.27.1"
},
"system_ssl": {
"version": "101010bf"
},
"urllib3": {
"version": "1.26.9"
},
"using_charset_normalizer": true,
"using_pyopenssl": false
}
Edit: added a couple words to the summary to clarify.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with requests/api.py and requests/sessions.py, especially the request and redirect paths shown in the traceback, then follow how urllib3 constructs the TLS SNI and Host header. Reproduce the issue with requests.get("https://pyropus.ca./") and verify that the Host header preserves the trailing dot while the request completes without a redirect loop.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, networking
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100