[4.0.x] Fixed #33132 -- Fixed test client handling of querystring only redirects.

Regression in 1e5aa8e1c7.

Backport of b1bf8c8a4b from main
This commit is contained in:
Jaap Roes 2021-09-23 12:18:15 +02:00 committed by Carlton Gibson
parent 715aa2db67
commit 5d36af6f6f
4 changed files with 27 additions and 1 deletions

View File

@ -835,8 +835,11 @@ class Client(ClientMixin, RequestFactory):
if url.port: if url.port:
extra['SERVER_PORT'] = str(url.port) extra['SERVER_PORT'] = str(url.port)
path = url.path
# RFC 2616: bare domains without path are treated as the root.
if not path and url.netloc:
path = '/'
# Prepend the request path to handle relative path redirects # Prepend the request path to handle relative path redirects
path = url.path or '/'
if not path.startswith('/'): if not path.startswith('/'):
path = urljoin(response.request['PATH_INFO'], path) path = urljoin(response.request['PATH_INFO'], path)

View File

@ -291,6 +291,13 @@ class ClientTest(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.request['PATH_INFO'], '/accounts/login/') self.assertEqual(response.request['PATH_INFO'], '/accounts/login/')
def test_redirect_to_querystring_only(self):
"""A URL that consists of a querystring only can be followed"""
response = self.client.post('/post_then_get_view/', follow=True)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.request['PATH_INFO'], '/post_then_get_view/')
self.assertEqual(response.content, b'The value of success is true.')
def test_follow_307_and_308_redirect(self): def test_follow_307_and_308_redirect(self):
""" """
A 307 or 308 redirect preserves the request method after the redirect. A 307 or 308 redirect preserves the request method after the redirect.

View File

@ -8,6 +8,7 @@ urlpatterns = [
path('upload_view/', views.upload_view, name='upload_view'), path('upload_view/', views.upload_view, name='upload_view'),
path('get_view/', views.get_view, name='get_view'), path('get_view/', views.get_view, name='get_view'),
path('post_view/', views.post_view), path('post_view/', views.post_view),
path('post_then_get_view/', views.post_then_get_view),
path('put_view/', views.put_view), path('put_view/', views.put_view),
path('trace_view/', views.trace_view), path('trace_view/', views.trace_view),
path('header_view/', views.view_with_header), path('header_view/', views.view_with_header),

View File

@ -85,6 +85,21 @@ def post_view(request):
return HttpResponse(t.render(c)) return HttpResponse(t.render(c))
def post_then_get_view(request):
"""
A view that expects a POST request, returns a redirect response
to itself providing only a ?success=true querystring,
the value of this querystring is then rendered upon GET.
"""
if request.method == 'POST':
return HttpResponseRedirect('?success=true')
t = Template('The value of success is {{ value }}.', name='GET Template')
c = Context({'value': request.GET.get('success', 'false')})
return HttpResponse(t.render(c))
def json_view(request): def json_view(request):
""" """
A view that expects a request with the header 'application/json' and JSON A view that expects a request with the header 'application/json' and JSON