[rh:requests] Apply remove_dot_segments to absolute redirect locations

Fixes https://github.com/yt-dlp/yt-dlp/issues/9020

Authored by: coletdjnz
This commit is contained in:
coletdjnz 2024-01-21 10:03:33 +13:00 committed by GitHub
parent f24e44e8cb
commit 35f4f764a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 21 additions and 9 deletions

View file

@ -180,6 +180,12 @@ def do_GET(self):
self.send_header('Location', '/a/b/./../../headers') self.send_header('Location', '/a/b/./../../headers')
self.send_header('Content-Length', '0') self.send_header('Content-Length', '0')
self.end_headers() self.end_headers()
elif self.path == '/redirect_dotsegments_absolute':
self.send_response(301)
# redirect to /headers but with dot segments before - absolute url
self.send_header('Location', f'http://127.0.0.1:{http_server_port(self.server)}/a/b/./../../headers')
self.send_header('Content-Length', '0')
self.end_headers()
elif self.path.startswith('/redirect_'): elif self.path.startswith('/redirect_'):
self._redirect() self._redirect()
elif self.path.startswith('/method'): elif self.path.startswith('/method'):
@ -345,16 +351,17 @@ def test_percent_encode(self, handler):
res.close() res.close()
@pytest.mark.parametrize('handler', ['Urllib', 'Requests'], indirect=True) @pytest.mark.parametrize('handler', ['Urllib', 'Requests'], indirect=True)
def test_remove_dot_segments(self, handler): @pytest.mark.parametrize('path', [
with handler() as rh: '/a/b/./../../headers',
'/redirect_dotsegments',
# https://github.com/yt-dlp/yt-dlp/issues/9020
'/redirect_dotsegments_absolute',
])
def test_remove_dot_segments(self, handler, path):
with handler(verbose=True) as rh:
# This isn't a comprehensive test, # This isn't a comprehensive test,
# but it should be enough to check whether the handler is removing dot segments # but it should be enough to check whether the handler is removing dot segments in required scenarios
res = validate_and_send(rh, Request(f'http://127.0.0.1:{self.http_port}/a/b/./../../headers')) res = validate_and_send(rh, Request(f'http://127.0.0.1:{self.http_port}{path}'))
assert res.status == 200
assert res.url == f'http://127.0.0.1:{self.http_port}/headers'
res.close()
res = validate_and_send(rh, Request(f'http://127.0.0.1:{self.http_port}/redirect_dotsegments'))
assert res.status == 200 assert res.status == 200
assert res.url == f'http://127.0.0.1:{self.http_port}/headers' assert res.url == f'http://127.0.0.1:{self.http_port}/headers'
res.close() res.close()

View file

@ -8,6 +8,7 @@
from ..dependencies import brotli, requests, urllib3 from ..dependencies import brotli, requests, urllib3
from ..utils import bug_reports_message, int_or_none, variadic from ..utils import bug_reports_message, int_or_none, variadic
from ..utils.networking import normalize_url
if requests is None: if requests is None:
raise ImportError('requests module is not installed') raise ImportError('requests module is not installed')
@ -199,6 +200,10 @@ def rebuild_method(self, prepared_request, response):
prepared_request.method = new_method prepared_request.method = new_method
# Requests fails to resolve dot segments on absolute redirect locations
# See: https://github.com/yt-dlp/yt-dlp/issues/9020
prepared_request.url = normalize_url(prepared_request.url)
def rebuild_auth(self, prepared_request, response): def rebuild_auth(self, prepared_request, response):
# HACK: undo status code change from rebuild_method, if applicable. # HACK: undo status code change from rebuild_method, if applicable.
# rebuild_auth runs after requests would remove headers/body based on status code # rebuild_auth runs after requests would remove headers/body based on status code