Fixed #21341 -- Eased https requests with the test client
All request methods of ``django.test.client.Client`` receive a ``secure`` argument that defaults to ``False`` indicating whether or not to make the request through https. Thanks Aymeric Augustin for the review.
This commit is contained in:
parent
19256f300e
commit
99b681e227
|
@ -269,60 +269,68 @@ class RequestFactory(object):
|
||||||
path = path.encode('utf-8').decode('iso-8859-1')
|
path = path.encode('utf-8').decode('iso-8859-1')
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def get(self, path, data={}, **extra):
|
def get(self, path, data={}, secure=False, **extra):
|
||||||
"Construct a GET request."
|
"Construct a GET request."
|
||||||
|
|
||||||
r = {
|
r = {
|
||||||
'QUERY_STRING': urlencode(data, doseq=True),
|
'QUERY_STRING': urlencode(data, doseq=True),
|
||||||
}
|
}
|
||||||
r.update(extra)
|
r.update(extra)
|
||||||
return self.generic('GET', path, **r)
|
return self.generic('GET', path, secure=secure, **r)
|
||||||
|
|
||||||
def post(self, path, data={}, content_type=MULTIPART_CONTENT,
|
def post(self, path, data={}, content_type=MULTIPART_CONTENT,
|
||||||
**extra):
|
secure=False, **extra):
|
||||||
"Construct a POST request."
|
"Construct a POST request."
|
||||||
|
|
||||||
post_data = self._encode_data(data, content_type)
|
post_data = self._encode_data(data, content_type)
|
||||||
|
|
||||||
return self.generic('POST', path, post_data, content_type, **extra)
|
return self.generic('POST', path, post_data, content_type,
|
||||||
|
secure=secure, **extra)
|
||||||
|
|
||||||
def head(self, path, data={}, **extra):
|
def head(self, path, data={}, secure=False, **extra):
|
||||||
"Construct a HEAD request."
|
"Construct a HEAD request."
|
||||||
|
|
||||||
r = {
|
r = {
|
||||||
'QUERY_STRING': urlencode(data, doseq=True),
|
'QUERY_STRING': urlencode(data, doseq=True),
|
||||||
}
|
}
|
||||||
r.update(extra)
|
r.update(extra)
|
||||||
return self.generic('HEAD', path, **r)
|
return self.generic('HEAD', path, secure=secure, **r)
|
||||||
|
|
||||||
def options(self, path, data='', content_type='application/octet-stream',
|
def options(self, path, data='', content_type='application/octet-stream',
|
||||||
**extra):
|
secure=False, **extra):
|
||||||
"Construct an OPTIONS request."
|
"Construct an OPTIONS request."
|
||||||
return self.generic('OPTIONS', path, data, content_type, **extra)
|
return self.generic('OPTIONS', path, data, content_type,
|
||||||
|
secure=secure, **extra)
|
||||||
|
|
||||||
def put(self, path, data='', content_type='application/octet-stream',
|
def put(self, path, data='', content_type='application/octet-stream',
|
||||||
**extra):
|
secure=False, **extra):
|
||||||
"Construct a PUT request."
|
"Construct a PUT request."
|
||||||
return self.generic('PUT', path, data, content_type, **extra)
|
return self.generic('PUT', path, data, content_type,
|
||||||
|
secure=secure, **extra)
|
||||||
|
|
||||||
def patch(self, path, data='', content_type='application/octet-stream',
|
def patch(self, path, data='', content_type='application/octet-stream',
|
||||||
**extra):
|
secure=False, **extra):
|
||||||
"Construct a PATCH request."
|
"Construct a PATCH request."
|
||||||
return self.generic('PATCH', path, data, content_type, **extra)
|
return self.generic('PATCH', path, data, content_type,
|
||||||
|
secure=secure, **extra)
|
||||||
|
|
||||||
def delete(self, path, data='', content_type='application/octet-stream',
|
def delete(self, path, data='', content_type='application/octet-stream',
|
||||||
**extra):
|
secure=False, **extra):
|
||||||
"Construct a DELETE request."
|
"Construct a DELETE request."
|
||||||
return self.generic('DELETE', path, data, content_type, **extra)
|
return self.generic('DELETE', path, data, content_type,
|
||||||
|
secure=secure, **extra)
|
||||||
|
|
||||||
def generic(self, method, path,
|
def generic(self, method, path, data='',
|
||||||
data='', content_type='application/octet-stream', **extra):
|
content_type='application/octet-stream', secure=False,
|
||||||
|
**extra):
|
||||||
"""Constructs an arbitrary HTTP request."""
|
"""Constructs an arbitrary HTTP request."""
|
||||||
parsed = urlparse(path)
|
parsed = urlparse(path)
|
||||||
data = force_bytes(data, settings.DEFAULT_CHARSET)
|
data = force_bytes(data, settings.DEFAULT_CHARSET)
|
||||||
r = {
|
r = {
|
||||||
'PATH_INFO': self._get_path(parsed),
|
'PATH_INFO': self._get_path(parsed),
|
||||||
'REQUEST_METHOD': str(method),
|
'REQUEST_METHOD': str(method),
|
||||||
|
'SERVER_PORT': str('443') if secure else str('80'),
|
||||||
|
'wsgi.url_scheme': str('https') if secure else str('http'),
|
||||||
}
|
}
|
||||||
if data:
|
if data:
|
||||||
r.update({
|
r.update({
|
||||||
|
@ -445,72 +453,82 @@ class Client(RequestFactory):
|
||||||
signals.template_rendered.disconnect(dispatch_uid=signal_uid)
|
signals.template_rendered.disconnect(dispatch_uid=signal_uid)
|
||||||
got_request_exception.disconnect(dispatch_uid="request-exception")
|
got_request_exception.disconnect(dispatch_uid="request-exception")
|
||||||
|
|
||||||
def get(self, path, data={}, follow=False, **extra):
|
def get(self, path, data={}, follow=False, secure=False, **extra):
|
||||||
"""
|
"""
|
||||||
Requests a response from the server using GET.
|
Requests a response from the server using GET.
|
||||||
"""
|
"""
|
||||||
response = super(Client, self).get(path, data=data, **extra)
|
response = super(Client, self).get(path, data=data, secure=secure,
|
||||||
|
**extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def post(self, path, data={}, content_type=MULTIPART_CONTENT,
|
def post(self, path, data={}, content_type=MULTIPART_CONTENT,
|
||||||
follow=False, **extra):
|
follow=False, secure=False, **extra):
|
||||||
"""
|
"""
|
||||||
Requests a response from the server using POST.
|
Requests a response from the server using POST.
|
||||||
"""
|
"""
|
||||||
response = super(Client, self).post(path, data=data, content_type=content_type, **extra)
|
response = super(Client, self).post(path, data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
secure=secure, **extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def head(self, path, data={}, follow=False, **extra):
|
def head(self, path, data={}, follow=False, secure=False, **extra):
|
||||||
"""
|
"""
|
||||||
Request a response from the server using HEAD.
|
Request a response from the server using HEAD.
|
||||||
"""
|
"""
|
||||||
response = super(Client, self).head(path, data=data, **extra)
|
response = super(Client, self).head(path, data=data, secure=secure,
|
||||||
|
**extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def options(self, path, data='', content_type='application/octet-stream',
|
def options(self, path, data='', content_type='application/octet-stream',
|
||||||
follow=False, **extra):
|
follow=False, secure=False, **extra):
|
||||||
"""
|
"""
|
||||||
Request a response from the server using OPTIONS.
|
Request a response from the server using OPTIONS.
|
||||||
"""
|
"""
|
||||||
response = super(Client, self).options(path, data=data, content_type=content_type, **extra)
|
response = super(Client, self).options(path, data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
secure=secure, **extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def put(self, path, data='', content_type='application/octet-stream',
|
def put(self, path, data='', content_type='application/octet-stream',
|
||||||
follow=False, **extra):
|
follow=False, secure=False, **extra):
|
||||||
"""
|
"""
|
||||||
Send a resource to the server using PUT.
|
Send a resource to the server using PUT.
|
||||||
"""
|
"""
|
||||||
response = super(Client, self).put(path, data=data, content_type=content_type, **extra)
|
response = super(Client, self).put(path, data=data,
|
||||||
|
content_type=content_type,
|
||||||
|
secure=secure, **extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def patch(self, path, data='', content_type='application/octet-stream',
|
def patch(self, path, data='', content_type='application/octet-stream',
|
||||||
follow=False, **extra):
|
follow=False, secure=False, **extra):
|
||||||
"""
|
"""
|
||||||
Send a resource to the server using PATCH.
|
Send a resource to the server using PATCH.
|
||||||
"""
|
"""
|
||||||
response = super(Client, self).patch(
|
response = super(Client, self).patch(path, data=data,
|
||||||
path, data=data, content_type=content_type, **extra)
|
content_type=content_type,
|
||||||
|
secure=secure, **extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def delete(self, path, data='', content_type='application/octet-stream',
|
def delete(self, path, data='', content_type='application/octet-stream',
|
||||||
follow=False, **extra):
|
follow=False, secure=False, **extra):
|
||||||
"""
|
"""
|
||||||
Send a DELETE request to the server.
|
Send a DELETE request to the server.
|
||||||
"""
|
"""
|
||||||
response = super(Client, self).delete(
|
response = super(Client, self).delete(path, data=data,
|
||||||
path, data=data, content_type=content_type, **extra)
|
content_type=content_type,
|
||||||
|
secure=secure, **extra)
|
||||||
if follow:
|
if follow:
|
||||||
response = self._handle_redirects(response, **extra)
|
response = self._handle_redirects(response, **extra)
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -444,6 +444,10 @@ Tests
|
||||||
client can't fetch externals URLs, this allows you to use ``assertRedirects``
|
client can't fetch externals URLs, this allows you to use ``assertRedirects``
|
||||||
with redirects that aren't part of your Django app.
|
with redirects that aren't part of your Django app.
|
||||||
|
|
||||||
|
* The ``secure`` argument was added to all the request methods of
|
||||||
|
:class:`~django.test.Client`. If ``True``, the request will be made
|
||||||
|
through HTTPS.
|
||||||
|
|
||||||
Backwards incompatible changes in 1.7
|
Backwards incompatible changes in 1.7
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
|
|
@ -431,8 +431,11 @@ Use the ``django.test.Client`` class to make requests.
|
||||||
Once you have a ``Client`` instance, you can call any of the following
|
Once you have a ``Client`` instance, you can call any of the following
|
||||||
methods:
|
methods:
|
||||||
|
|
||||||
.. method:: Client.get(path, data={}, follow=False, **extra)
|
.. method:: Client.get(path, data={}, follow=False, secure=False, **extra)
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
The ``secure`` argument was added.
|
||||||
|
|
||||||
Makes a GET request on the provided ``path`` and returns a ``Response``
|
Makes a GET request on the provided ``path`` and returns a ``Response``
|
||||||
object, which is documented below.
|
object, which is documented below.
|
||||||
|
@ -488,7 +491,10 @@ Use the ``django.test.Client`` class to make requests.
|
||||||
>>> response.redirect_chain
|
>>> response.redirect_chain
|
||||||
[(u'http://testserver/next/', 302), (u'http://testserver/final/', 302)]
|
[(u'http://testserver/next/', 302), (u'http://testserver/final/', 302)]
|
||||||
|
|
||||||
.. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT, follow=False, **extra)
|
If you set ``secure`` to ``True`` the client will emulate an HTTPS
|
||||||
|
request.
|
||||||
|
|
||||||
|
.. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT, follow=False, secure=False, **extra)
|
||||||
|
|
||||||
Makes a POST request on the provided ``path`` and returns a
|
Makes a POST request on the provided ``path`` and returns a
|
||||||
``Response`` object, which is documented below.
|
``Response`` object, which is documented below.
|
||||||
|
@ -562,14 +568,17 @@ Use the ``django.test.Client`` class to make requests.
|
||||||
and a ``redirect_chain`` attribute will be set in the response object
|
and a ``redirect_chain`` attribute will be set in the response object
|
||||||
containing tuples of the intermediate urls and status codes.
|
containing tuples of the intermediate urls and status codes.
|
||||||
|
|
||||||
.. method:: Client.head(path, data={}, follow=False, **extra)
|
If you set ``secure`` to ``True`` the client will emulate an HTTPS
|
||||||
|
request.
|
||||||
|
|
||||||
|
.. method:: Client.head(path, data={}, follow=False, secure=False, **extra)
|
||||||
|
|
||||||
Makes a HEAD request on the provided ``path`` and returns a
|
Makes a HEAD request on the provided ``path`` and returns a
|
||||||
``Response`` object. This method works just like :meth:`Client.get`,
|
``Response`` object. This method works just like :meth:`Client.get`,
|
||||||
including the ``follow`` and ``extra`` arguments, except it does not
|
including the ``follow``, ``secure`` and ``extra`` arguments, except
|
||||||
return a message body.
|
it does not return a message body.
|
||||||
|
|
||||||
.. method:: Client.options(path, data='', content_type='application/octet-stream', follow=False, **extra)
|
.. method:: Client.options(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
|
||||||
|
|
||||||
Makes an OPTIONS request on the provided ``path`` and returns a
|
Makes an OPTIONS request on the provided ``path`` and returns a
|
||||||
``Response`` object. Useful for testing RESTful interfaces.
|
``Response`` object. Useful for testing RESTful interfaces.
|
||||||
|
@ -577,10 +586,10 @@ Use the ``django.test.Client`` class to make requests.
|
||||||
When ``data`` is provided, it is used as the request body, and
|
When ``data`` is provided, it is used as the request body, and
|
||||||
a ``Content-Type`` header is set to ``content_type``.
|
a ``Content-Type`` header is set to ``content_type``.
|
||||||
|
|
||||||
The ``follow`` and ``extra`` arguments act the same as for
|
The ``follow``, ``secure`` and ``extra`` arguments act the same as for
|
||||||
:meth:`Client.get`.
|
:meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.put(path, data='', content_type='application/octet-stream', follow=False, **extra)
|
.. method:: Client.put(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
|
||||||
|
|
||||||
Makes a PUT request on the provided ``path`` and returns a
|
Makes a PUT request on the provided ``path`` and returns a
|
||||||
``Response`` object. Useful for testing RESTful interfaces.
|
``Response`` object. Useful for testing RESTful interfaces.
|
||||||
|
@ -588,18 +597,18 @@ Use the ``django.test.Client`` class to make requests.
|
||||||
When ``data`` is provided, it is used as the request body, and
|
When ``data`` is provided, it is used as the request body, and
|
||||||
a ``Content-Type`` header is set to ``content_type``.
|
a ``Content-Type`` header is set to ``content_type``.
|
||||||
|
|
||||||
The ``follow`` and ``extra`` arguments act the same as for
|
The ``follow``, ``secure`` and ``extra`` arguments act the same as for
|
||||||
:meth:`Client.get`.
|
:meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.patch(path, data='', content_type='application/octet-stream', follow=False, **extra)
|
.. method:: Client.patch(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
|
||||||
|
|
||||||
Makes a PATCH request on the provided ``path`` and returns a
|
Makes a PATCH request on the provided ``path`` and returns a
|
||||||
``Response`` object. Useful for testing RESTful interfaces.
|
``Response`` object. Useful for testing RESTful interfaces.
|
||||||
|
|
||||||
The ``follow`` and ``extra`` arguments act the same as for
|
The ``follow``, ``secure`` and ``extra`` arguments act the same as for
|
||||||
:meth:`Client.get`.
|
:meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.delete(path, data='', content_type='application/octet-stream', follow=False, **extra)
|
.. method:: Client.delete(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
|
||||||
|
|
||||||
Makes an DELETE request on the provided ``path`` and returns a
|
Makes an DELETE request on the provided ``path`` and returns a
|
||||||
``Response`` object. Useful for testing RESTful interfaces.
|
``Response`` object. Useful for testing RESTful interfaces.
|
||||||
|
@ -607,7 +616,7 @@ Use the ``django.test.Client`` class to make requests.
|
||||||
When ``data`` is provided, it is used as the request body, and
|
When ``data`` is provided, it is used as the request body, and
|
||||||
a ``Content-Type`` header is set to ``content_type``.
|
a ``Content-Type`` header is set to ``content_type``.
|
||||||
|
|
||||||
The ``follow`` and ``extra`` arguments act the same as for
|
The ``follow``, ``secure`` and ``extra`` arguments act the same as for
|
||||||
:meth:`Client.get`.
|
:meth:`Client.get`.
|
||||||
|
|
||||||
.. method:: Client.login(**credentials)
|
.. method:: Client.login(**credentials)
|
||||||
|
|
|
@ -93,6 +93,18 @@ class ClientTest(TestCase):
|
||||||
self.assertEqual(response.templates[0].name, "Book template")
|
self.assertEqual(response.templates[0].name, "Book template")
|
||||||
self.assertEqual(response.content, b"Blink - Malcolm Gladwell")
|
self.assertEqual(response.content, b"Blink - Malcolm Gladwell")
|
||||||
|
|
||||||
|
def test_insecure(self):
|
||||||
|
"GET a URL through http"
|
||||||
|
response = self.client.get('/test_client/secure_view/', secure=False)
|
||||||
|
self.assertFalse(response.test_was_secure_request)
|
||||||
|
self.assertEqual(response.test_server_port, '80')
|
||||||
|
|
||||||
|
def test_secure(self):
|
||||||
|
"GET a URL through https"
|
||||||
|
response = self.client.get('/test_client/secure_view/', secure=True)
|
||||||
|
self.assertTrue(response.test_was_secure_request)
|
||||||
|
self.assertEqual(response.test_server_port, '443')
|
||||||
|
|
||||||
def test_redirect(self):
|
def test_redirect(self):
|
||||||
"GET a URL that redirects elsewhere"
|
"GET a URL that redirects elsewhere"
|
||||||
response = self.client.get('/test_client/redirect_view/')
|
response = self.client.get('/test_client/redirect_view/')
|
||||||
|
|
|
@ -68,6 +68,7 @@ def view_with_secure(request):
|
||||||
"A view that indicates if the request was secure"
|
"A view that indicates if the request was secure"
|
||||||
response = HttpResponse()
|
response = HttpResponse()
|
||||||
response.test_was_secure_request = request.is_secure()
|
response.test_was_secure_request = request.is_secure()
|
||||||
|
response.test_server_port = request.META.get('SERVER_PORT', 80)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def double_redirect_view(request):
|
def double_redirect_view(request):
|
||||||
|
|
Loading…
Reference in New Issue