Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow query params in redirects #5081

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions readthedocs/core/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""URL resolver for documentation."""

import re
from urllib.parse import urlunparse

from django.conf import settings

Expand Down Expand Up @@ -159,8 +160,8 @@ def resolve_domain(self, project, private=None):
return getattr(settings, 'PRODUCTION_DOMAIN')

def resolve(
self, project, require_https=False, filename='', private=None,
**kwargs
self, project, require_https=False, filename='', query_params='',
private=None, **kwargs
):
if private is None:
version_slug = kwargs.get('version_slug')
Expand Down Expand Up @@ -192,12 +193,10 @@ def resolve(
])
protocol = 'https' if use_https_protocol else 'http'

return '{protocol}://{domain}{path}'.format(
protocol=protocol,
domain=domain,
path=self.
resolve_path(project, filename=filename, private=private, **kwargs),
path = self.resolve_path(
project, filename=filename, private=private, **kwargs
)
return urlunparse((protocol, domain, path, '', query_params, ''))
stsewd marked this conversation as resolved.
Show resolved Hide resolved

def _get_canonical_project(self, project, projects=None):
"""
Expand Down
2 changes: 1 addition & 1 deletion readthedocs/core/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def server_error_404(request, exception=None, template_name='404.html'): # pyli

Marking exception as optional to make /404/ testing page to work.
"""
response = get_redirect_response(request, path=request.get_full_path())
response = get_redirect_response(request, full_path=request.get_full_path())

if response:
if response.url == request.build_absolute_uri():
Expand Down
16 changes: 14 additions & 2 deletions readthedocs/core/views/serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import mimetypes
import os
from functools import wraps
from urllib.parse import urlparse

from django.conf import settings
from django.http import Http404, HttpResponse, HttpResponseRedirect
Expand Down Expand Up @@ -107,15 +108,26 @@ def inner_view( # noqa
@map_subproject_slug
def redirect_project_slug(request, project, subproject): # pylint: disable=unused-argument
"""Handle / -> /en/latest/ directs on subdomains."""
return HttpResponseRedirect(resolve(subproject or project))
urlparse_result = urlparse(request.get_full_path())
return HttpResponseRedirect(
resolve(
subproject or project,
query_params=urlparse_result.query,
)
)


@map_project_slug
@map_subproject_slug
def redirect_page_with_filename(request, project, subproject, filename): # pylint: disable=unused-argument # noqa
"""Redirect /page/file.html to /en/latest/file.html."""
urlparse_result = urlparse(request.get_full_path())
return HttpResponseRedirect(
resolve(subproject or project, filename=filename),
resolve(
subproject or project,
filename=filename,
query_params=urlparse_result.query,
)
)


Expand Down
1 change: 1 addition & 0 deletions readthedocs/redirects/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def get_redirect_path(self, path, language=None, version_slug=None):
)
if new_path:
return new_path
return None


RedirectManager = Manager.from_queryset(RedirectQuerySet)
18 changes: 9 additions & 9 deletions readthedocs/redirects/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-

"""
Redirection view support.

Expand All @@ -11,6 +9,7 @@
"""
import logging
import re
from urllib.parse import urlparse, urlunparse

from django.http import HttpResponseRedirect

Expand Down Expand Up @@ -69,25 +68,26 @@ def language_and_version_from_path(path):
return None, None, path


def get_redirect_response(request, path):
project, path = project_and_path_from_request(request, path)
def get_redirect_response(request, full_path):
project, full_path = project_and_path_from_request(request, full_path)
if not project:
return None

language = None
version_slug = None
schema, netloc, path, params, query, fragments = urlparse(full_path)
if not project.single_version:
language, version_slug, path = language_and_version_from_path(path)

new_path = project.redirects.get_redirect_path(
path=path,
language=language,
version_slug=version_slug,
path = project.redirects.get_redirect_path(
path=path, language=language, version_slug=version_slug
)

if new_path is None:
if path is None:
return None

new_path = urlunparse((schema, netloc, path, params, query, fragments))

# Re-use the domain and protocol used in the current request.
# Redirects shouldn't change the domain, version or language.
new_path = request.build_absolute_uri(new_path)
Expand Down
32 changes: 32 additions & 0 deletions readthedocs/rtd_tests/tests/test_redirects.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ def test_proper_page_on_main_site(self):
'http://readthedocs.org/docs/pip/en/latest/test.html',
)

def test_page_redirect_with_query_params(self):
r = self.client.get('/docs/pip/page/test.html?foo=bar')
self.assertEqual(r.status_code, 302)
self.assertEqual(
r['Location'],
'http://readthedocs.org/docs/pip/en/latest/test.html?foo=bar'
)

# If slug is neither valid lang nor valid version, it should 404.
# TODO: This should 404 directly, not redirect first
def test_improper_url_with_nonexistent_slug(self):
Expand Down Expand Up @@ -97,6 +105,15 @@ def test_proper_subdomain(self):
r['Location'], 'http://pip.readthedocs.org/en/latest/',
)

@override_settings(USE_SUBDOMAIN=True)
def test_root_redirect_with_query_params(self):
r = self.client.get('/?foo=bar', HTTP_HOST='pip.readthedocs.org')
self.assertEqual(r.status_code, 302)
self.assertEqual(
r['Location'],
'http://pip.readthedocs.org/en/latest/?foo=bar'
)

# Specific Page Redirects
@override_settings(USE_SUBDOMAIN=True)
def test_proper_page_on_subdomain(self):
Expand Down Expand Up @@ -190,6 +207,21 @@ def test_redirect_page(self):
r['Location'], 'http://pip.readthedocs.org/en/latest/tutorial/install.html',
)

@override_settings(USE_SUBDOMAIN=True)
def test_redirect_with_query_params(self):
Redirect.objects.create(
project=self.pip, redirect_type='page',
from_url='/install.html', to_url='/tutorial/install.html'
)
r = self.client.get(
'/install.html?foo=bar', HTTP_HOST='pip.readthedocs.org'
)
self.assertEqual(r.status_code, 302)
self.assertEqual(
r['Location'],
'http://pip.readthedocs.org/en/latest/tutorial/install.html?foo=bar'
stsewd marked this conversation as resolved.
Show resolved Hide resolved
)

@override_settings(USE_SUBDOMAIN=True)
def test_redirect_exact(self):
Redirect.objects.create(
Expand Down