Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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.
  • Loading branch information...
commit 99b681e227b5b7880d6edd0d8dd670034d431859 1 parent 19256f3
Unai Zalakain authored October 28, 2013 claudep committed November 02, 2013
82  django/test/client.py
@@ -269,60 +269,68 @@ def _get_path(self, parsed):
269 269
             path = path.encode('utf-8').decode('iso-8859-1')
270 270
         return path
271 271
 
272  
-    def get(self, path, data={}, **extra):
  272
+    def get(self, path, data={}, secure=False, **extra):
273 273
         "Construct a GET request."
274 274
 
275 275
         r = {
276 276
             'QUERY_STRING': urlencode(data, doseq=True),
277 277
         }
278 278
         r.update(extra)
279  
-        return self.generic('GET', path, **r)
  279
+        return self.generic('GET', path, secure=secure, **r)
280 280
 
281 281
     def post(self, path, data={}, content_type=MULTIPART_CONTENT,
282  
-             **extra):
  282
+             secure=False, **extra):
283 283
         "Construct a POST request."
284 284
 
285 285
         post_data = self._encode_data(data, content_type)
286 286
 
287  
-        return self.generic('POST', path, post_data, content_type, **extra)
  287
+        return self.generic('POST', path, post_data, content_type,
  288
+                            secure=secure, **extra)
288 289
 
289  
-    def head(self, path, data={}, **extra):
  290
+    def head(self, path, data={}, secure=False, **extra):
290 291
         "Construct a HEAD request."
291 292
 
292 293
         r = {
293 294
             'QUERY_STRING': urlencode(data, doseq=True),
294 295
         }
295 296
         r.update(extra)
296  
-        return self.generic('HEAD', path, **r)
  297
+        return self.generic('HEAD', path, secure=secure, **r)
297 298
 
298 299
     def options(self, path, data='', content_type='application/octet-stream',
299  
-                **extra):
  300
+                secure=False, **extra):
300 301
         "Construct an OPTIONS request."
301  
-        return self.generic('OPTIONS', path, data, content_type, **extra)
  302
+        return self.generic('OPTIONS', path, data, content_type,
  303
+                            secure=secure, **extra)
302 304
 
303 305
     def put(self, path, data='', content_type='application/octet-stream',
304  
-            **extra):
  306
+            secure=False, **extra):
305 307
         "Construct a PUT request."
306  
-        return self.generic('PUT', path, data, content_type, **extra)
  308
+        return self.generic('PUT', path, data, content_type,
  309
+                            secure=secure, **extra)
307 310
 
308 311
     def patch(self, path, data='', content_type='application/octet-stream',
309  
-              **extra):
  312
+              secure=False, **extra):
310 313
         "Construct a PATCH request."
311  
-        return self.generic('PATCH', path, data, content_type, **extra)
  314
+        return self.generic('PATCH', path, data, content_type,
  315
+                            secure=secure, **extra)
312 316
 
313 317
     def delete(self, path, data='', content_type='application/octet-stream',
314  
-               **extra):
  318
+               secure=False, **extra):
315 319
         "Construct a DELETE request."
316  
-        return self.generic('DELETE', path, data, content_type, **extra)
  320
+        return self.generic('DELETE', path, data, content_type,
  321
+                            secure=secure, **extra)
317 322
 
318  
-    def generic(self, method, path,
319  
-                data='', content_type='application/octet-stream', **extra):
  323
+    def generic(self, method, path, data='',
  324
+                content_type='application/octet-stream', secure=False,
  325
+                **extra):
320 326
         """Constructs an arbitrary HTTP request."""
321 327
         parsed = urlparse(path)
322 328
         data = force_bytes(data, settings.DEFAULT_CHARSET)
323 329
         r = {
324 330
             'PATH_INFO':      self._get_path(parsed),
325 331
             'REQUEST_METHOD': str(method),
  332
+            'SERVER_PORT': str('443') if secure else str('80'),
  333
+            'wsgi.url_scheme': str('https') if secure else str('http'),
326 334
         }
327 335
         if data:
328 336
             r.update({
@@ -445,72 +453,82 @@ def request(self, **request):
445 453
             signals.template_rendered.disconnect(dispatch_uid=signal_uid)
446 454
             got_request_exception.disconnect(dispatch_uid="request-exception")
447 455
 
448  
-    def get(self, path, data={}, follow=False, **extra):
  456
+    def get(self, path, data={}, follow=False, secure=False, **extra):
449 457
         """
450 458
         Requests a response from the server using GET.
451 459
         """
452  
-        response = super(Client, self).get(path, data=data, **extra)
  460
+        response = super(Client, self).get(path, data=data, secure=secure,
  461
+                                           **extra)
453 462
         if follow:
454 463
             response = self._handle_redirects(response, **extra)
455 464
         return response
456 465
 
457 466
     def post(self, path, data={}, content_type=MULTIPART_CONTENT,
458  
-             follow=False, **extra):
  467
+             follow=False, secure=False, **extra):
459 468
         """
460 469
         Requests a response from the server using POST.
461 470
         """
462  
-        response = super(Client, self).post(path, data=data, content_type=content_type, **extra)
  471
+        response = super(Client, self).post(path, data=data,
  472
+                                            content_type=content_type,
  473
+                                            secure=secure, **extra)
463 474
         if follow:
464 475
             response = self._handle_redirects(response, **extra)
465 476
         return response
466 477
 
467  
-    def head(self, path, data={}, follow=False, **extra):
  478
+    def head(self, path, data={}, follow=False, secure=False, **extra):
468 479
         """
469 480
         Request a response from the server using HEAD.
470 481
         """
471  
-        response = super(Client, self).head(path, data=data, **extra)
  482
+        response = super(Client, self).head(path, data=data, secure=secure,
  483
+                                            **extra)
472 484
         if follow:
473 485
             response = self._handle_redirects(response, **extra)
474 486
         return response
475 487
 
476 488
     def options(self, path, data='', content_type='application/octet-stream',
477  
-                follow=False, **extra):
  489
+                follow=False, secure=False, **extra):
478 490
         """
479 491
         Request a response from the server using OPTIONS.
480 492
         """
481  
-        response = super(Client, self).options(path, data=data, content_type=content_type, **extra)
  493
+        response = super(Client, self).options(path, data=data,
  494
+                                               content_type=content_type,
  495
+                                               secure=secure, **extra)
482 496
         if follow:
483 497
             response = self._handle_redirects(response, **extra)
484 498
         return response
485 499
 
486 500
     def put(self, path, data='', content_type='application/octet-stream',
487  
-            follow=False, **extra):
  501
+            follow=False, secure=False, **extra):
488 502
         """
489 503
         Send a resource to the server using PUT.
490 504
         """
491  
-        response = super(Client, self).put(path, data=data, content_type=content_type, **extra)
  505
+        response = super(Client, self).put(path, data=data,
  506
+                                           content_type=content_type,
  507
+                                           secure=secure, **extra)
492 508
         if follow:
493 509
             response = self._handle_redirects(response, **extra)
494 510
         return response
495 511
 
496 512
     def patch(self, path, data='', content_type='application/octet-stream',
497  
-              follow=False, **extra):
  513
+              follow=False, secure=False, **extra):
498 514
         """
499 515
         Send a resource to the server using PATCH.
500 516
         """
501  
-        response = super(Client, self).patch(
502  
-            path, data=data, content_type=content_type, **extra)
  517
+        response = super(Client, self).patch(path, data=data,
  518
+                                             content_type=content_type,
  519
+                                             secure=secure, **extra)
503 520
         if follow:
504 521
             response = self._handle_redirects(response, **extra)
505 522
         return response
506 523
 
507 524
     def delete(self, path, data='', content_type='application/octet-stream',
508  
-               follow=False, **extra):
  525
+               follow=False, secure=False, **extra):
509 526
         """
510 527
         Send a DELETE request to the server.
511 528
         """
512  
-        response = super(Client, self).delete(
513  
-            path, data=data, content_type=content_type, **extra)
  529
+        response = super(Client, self).delete(path, data=data,
  530
+                                              content_type=content_type,
  531
+                                              secure=secure, **extra)
514 532
         if follow:
515 533
             response = self._handle_redirects(response, **extra)
516 534
         return response
4  docs/releases/1.7.txt
@@ -444,6 +444,10 @@ Tests
444 444
   client can't fetch externals URLs, this allows you to use ``assertRedirects``
445 445
   with redirects that aren't part of your Django app.
446 446
 
  447
+* The ``secure`` argument was added to all the request methods of
  448
+  :class:`~django.test.Client`. If ``True``, the request will be made
  449
+  through HTTPS.
  450
+
447 451
 Backwards incompatible changes in 1.7
448 452
 =====================================
449 453
 
35  docs/topics/testing/overview.txt
@@ -431,8 +431,11 @@ Use the ``django.test.Client`` class to make requests.
431 431
     Once you have a ``Client`` instance, you can call any of the following
432 432
     methods:
433 433
 
434  
-    .. method:: Client.get(path, data={}, follow=False, **extra)
  434
+    .. method:: Client.get(path, data={}, follow=False, secure=False, **extra)
435 435
 
  436
+        .. versionadded:: 1.7
  437
+
  438
+            The ``secure`` argument was added.
436 439
 
437 440
         Makes a GET request on the provided ``path`` and returns a ``Response``
438 441
         object, which is documented below.
@@ -488,7 +491,10 @@ Use the ``django.test.Client`` class to make requests.
488 491
             >>> response.redirect_chain
489 492
             [(u'http://testserver/next/', 302), (u'http://testserver/final/', 302)]
490 493
 
491  
-    .. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT, follow=False, **extra)
  494
+        If you set ``secure`` to ``True`` the client will emulate an HTTPS
  495
+        request.
  496
+
  497
+    .. method:: Client.post(path, data={}, content_type=MULTIPART_CONTENT, follow=False, secure=False, **extra)
492 498
 
493 499
         Makes a POST request on the provided ``path`` and returns a
494 500
         ``Response`` object, which is documented below.
@@ -562,14 +568,17 @@ Use the ``django.test.Client`` class to make requests.
562 568
         and a ``redirect_chain`` attribute will be set in the response object
563 569
         containing tuples of the intermediate urls and status codes.
564 570
 
565  
-    .. method:: Client.head(path, data={}, follow=False, **extra)
  571
+        If you set ``secure`` to ``True`` the client will emulate an HTTPS
  572
+        request.
  573
+
  574
+    .. method:: Client.head(path, data={}, follow=False, secure=False, **extra)
566 575
 
567 576
         Makes a HEAD request on the provided ``path`` and returns a
568 577
         ``Response`` object. This method works just like :meth:`Client.get`,
569  
-        including the ``follow`` and ``extra`` arguments, except it does not
570  
-        return a message body.
  578
+        including the ``follow``, ``secure`` and ``extra`` arguments, except
  579
+        it does not return a message body.
571 580
 
572  
-    .. method:: Client.options(path, data='', content_type='application/octet-stream', follow=False, **extra)
  581
+    .. method:: Client.options(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
573 582
 
574 583
         Makes an OPTIONS request on the provided ``path`` and returns a
575 584
         ``Response`` object. Useful for testing RESTful interfaces.
@@ -577,10 +586,10 @@ Use the ``django.test.Client`` class to make requests.
577 586
         When ``data`` is provided, it is used as the request body, and
578 587
         a ``Content-Type`` header is set to ``content_type``.
579 588
 
580  
-        The ``follow`` and ``extra`` arguments act the same as for
  589
+        The ``follow``, ``secure`` and ``extra`` arguments act the same as for
581 590
         :meth:`Client.get`.
582 591
 
583  
-    .. method:: Client.put(path, data='', content_type='application/octet-stream', follow=False, **extra)
  592
+    .. method:: Client.put(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
584 593
 
585 594
         Makes a PUT request on the provided ``path`` and returns a
586 595
         ``Response`` object. Useful for testing RESTful interfaces.
@@ -588,18 +597,18 @@ Use the ``django.test.Client`` class to make requests.
588 597
         When ``data`` is provided, it is used as the request body, and
589 598
         a ``Content-Type`` header is set to ``content_type``.
590 599
 
591  
-        The ``follow`` and ``extra`` arguments act the same as for
  600
+        The ``follow``, ``secure`` and ``extra`` arguments act the same as for
592 601
         :meth:`Client.get`.
593 602
 
594  
-    .. method:: Client.patch(path, data='', content_type='application/octet-stream', follow=False, **extra)
  603
+    .. method:: Client.patch(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
595 604
 
596 605
         Makes a PATCH request on the provided ``path`` and returns a
597 606
         ``Response`` object. Useful for testing RESTful interfaces.
598 607
 
599  
-        The ``follow`` and ``extra`` arguments act the same as for
  608
+        The ``follow``, ``secure`` and ``extra`` arguments act the same as for
600 609
         :meth:`Client.get`.
601 610
 
602  
-    .. method:: Client.delete(path, data='', content_type='application/octet-stream', follow=False, **extra)
  611
+    .. method:: Client.delete(path, data='', content_type='application/octet-stream', follow=False, secure=False, **extra)
603 612
 
604 613
         Makes an DELETE request on the provided ``path`` and returns a
605 614
         ``Response`` object. Useful for testing RESTful interfaces.
@@ -607,7 +616,7 @@ Use the ``django.test.Client`` class to make requests.
607 616
         When ``data`` is provided, it is used as the request body, and
608 617
         a ``Content-Type`` header is set to ``content_type``.
609 618
 
610  
-        The ``follow`` and ``extra`` arguments act the same as for
  619
+        The ``follow``, ``secure`` and ``extra`` arguments act the same as for
611 620
         :meth:`Client.get`.
612 621
 
613 622
     .. method:: Client.login(**credentials)
12  tests/test_client/tests.py
@@ -93,6 +93,18 @@ def test_raw_post(self):
93 93
         self.assertEqual(response.templates[0].name, "Book template")
94 94
         self.assertEqual(response.content, b"Blink - Malcolm Gladwell")
95 95
 
  96
+    def test_insecure(self):
  97
+        "GET a URL through http"
  98
+        response = self.client.get('/test_client/secure_view/', secure=False)
  99
+        self.assertFalse(response.test_was_secure_request)
  100
+        self.assertEqual(response.test_server_port, '80')
  101
+
  102
+    def test_secure(self):
  103
+        "GET a URL through https"
  104
+        response = self.client.get('/test_client/secure_view/', secure=True)
  105
+        self.assertTrue(response.test_was_secure_request)
  106
+        self.assertEqual(response.test_server_port, '443')
  107
+
96 108
     def test_redirect(self):
97 109
         "GET a URL that redirects elsewhere"
98 110
         response = self.client.get('/test_client/redirect_view/')
1  tests/test_client/views.py
@@ -68,6 +68,7 @@ def view_with_secure(request):
68 68
     "A view that indicates if the request was secure"
69 69
     response = HttpResponse()
70 70
     response.test_was_secure_request = request.is_secure()
  71
+    response.test_server_port = request.META.get('SERVER_PORT', 80)
71 72
     return response
72 73
 
73 74
 def double_redirect_view(request):

0 notes on commit 99b681e

Please sign in to comment.
Something went wrong with that request. Please try again.