Commit 99b681e2 authored by Unai Zalakain's avatar Unai Zalakain Committed by Claude Paroz
Browse files

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.
parent 19256f30
Loading
Loading
Loading
Loading
+50 −32
Original line number Diff line number Diff line
@@ -269,60 +269,68 @@ class RequestFactory(object):
            path = path.encode('utf-8').decode('iso-8859-1')
        return path

    def get(self, path, data={}, **extra):
    def get(self, path, data={}, secure=False, **extra):
        "Construct a GET request."

        r = {
            'QUERY_STRING': urlencode(data, doseq=True),
        }
        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,
             **extra):
             secure=False, **extra):
        "Construct a POST request."

        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."

        r = {
            'QUERY_STRING': urlencode(data, doseq=True),
        }
        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',
                **extra):
                secure=False, **extra):
        "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',
            **extra):
            secure=False, **extra):
        "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',
              **extra):
              secure=False, **extra):
        "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',
               **extra):
               secure=False, **extra):
        "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,
                data='', content_type='application/octet-stream', **extra):
    def generic(self, method, path, data='',
                content_type='application/octet-stream', secure=False,
                **extra):
        """Constructs an arbitrary HTTP request."""
        parsed = urlparse(path)
        data = force_bytes(data, settings.DEFAULT_CHARSET)
        r = {
            'PATH_INFO':      self._get_path(parsed),
            '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:
            r.update({
@@ -445,72 +453,82 @@ class Client(RequestFactory):
            signals.template_rendered.disconnect(dispatch_uid=signal_uid)
            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.
        """
        response = super(Client, self).get(path, data=data, **extra)
        response = super(Client, self).get(path, data=data, secure=secure,
                                           **extra)
        if follow:
            response = self._handle_redirects(response, **extra)
        return response

    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.
        """
        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:
            response = self._handle_redirects(response, **extra)
        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.
        """
        response = super(Client, self).head(path, data=data, **extra)
        response = super(Client, self).head(path, data=data, secure=secure,
                                            **extra)
        if follow:
            response = self._handle_redirects(response, **extra)
        return response

    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.
        """
        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:
            response = self._handle_redirects(response, **extra)
        return response

    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.
        """
        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:
            response = self._handle_redirects(response, **extra)
        return response

    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.
        """
        response = super(Client, self).patch(
            path, data=data, content_type=content_type, **extra)
        response = super(Client, self).patch(path, data=data,
                                             content_type=content_type,
                                             secure=secure, **extra)
        if follow:
            response = self._handle_redirects(response, **extra)
        return response

    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.
        """
        response = super(Client, self).delete(
            path, data=data, content_type=content_type, **extra)
        response = super(Client, self).delete(path, data=data,
                                              content_type=content_type,
                                              secure=secure, **extra)
        if follow:
            response = self._handle_redirects(response, **extra)
        return response
+4 −0
Original line number Diff line number Diff line
@@ -444,6 +444,10 @@ Tests
  client can't fetch externals URLs, this allows you to use ``assertRedirects``
  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
=====================================

+22 −13
Original line number Diff line number Diff line
@@ -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
    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``
        object, which is documented below.
@@ -488,7 +491,10 @@ Use the ``django.test.Client`` class to make requests.
            >>> response.redirect_chain
            [(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
        ``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
        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
        ``Response`` object. This method works just like :meth:`Client.get`,
        including the ``follow`` and ``extra`` arguments, except it does not
        return a message body.
        including the ``follow``, ``secure`` and ``extra`` arguments, except
        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
        ``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
        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`.

    .. 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
        ``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
        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`.

    .. 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
        ``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`.

    .. 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
        ``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
        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`.

    .. method:: Client.login(**credentials)
+12 −0
Original line number Diff line number Diff line
@@ -93,6 +93,18 @@ class ClientTest(TestCase):
        self.assertEqual(response.templates[0].name, "Book template")
        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):
        "GET a URL that redirects elsewhere"
        response = self.client.get('/test_client/redirect_view/')
+1 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ def view_with_secure(request):
    "A view that indicates if the request was secure"
    response = HttpResponse()
    response.test_was_secure_request = request.is_secure()
    response.test_server_port = request.META.get('SERVER_PORT', 80)
    return response

def double_redirect_view(request):