]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Add ssl_options argument for simple_httpclient.
authorBen Darnell <ben@bendarnell.com>
Sun, 15 Feb 2015 23:04:52 +0000 (18:04 -0500)
committerBen Darnell <ben@bendarnell.com>
Sun, 15 Feb 2015 23:04:52 +0000 (18:04 -0500)
tornado/curl_httpclient.py
tornado/httpclient.py
tornado/simple_httpclient.py
tornado/test/simple_httpclient_test.py

index bced9499efacc7a735505c2a84afe75fe78cf8ce..87312de236c4823d9cb785bd3ff5c847442f76af 100644 (file)
@@ -417,6 +417,9 @@ class CurlAsyncHTTPClient(AsyncHTTPClient):
         if request.client_key is not None:
             curl.setopt(pycurl.SSLKEY, request.client_key)
 
+        if request.ssl_options is not None:
+            raise ValueError("ssl_options not supported in curl_httpclient")
+
         if threading.activeCount() > 1:
             # libcurl/pycurl is not thread-safe by default.  When multiple threads
             # are used, signals should be disabled.  This has the side effect
index 0ae9e4802fba353cd62e5854813232f88540561f..87b21437d5e765b7c1449dc7c06bd216a3e761f4 100644 (file)
@@ -310,7 +310,8 @@ class HTTPRequest(object):
                  validate_cert=None, ca_certs=None,
                  allow_ipv6=None,
                  client_key=None, client_cert=None, body_producer=None,
-                 expect_100_continue=False, decompress_response=None):
+                 expect_100_continue=False, decompress_response=None,
+                 ssl_options=None):
         r"""All parameters except ``url`` are optional.
 
         :arg string url: URL to fetch
@@ -380,12 +381,16 @@ class HTTPRequest(object):
         :arg string ca_certs: filename of CA certificates in PEM format,
            or None to use defaults.  See note below when used with
            ``curl_httpclient``.
-        :arg bool allow_ipv6: Use IPv6 when available?  Default is false in
-           ``simple_httpclient`` and true in ``curl_httpclient``
         :arg string client_key: Filename for client SSL key, if any.  See
            note below when used with ``curl_httpclient``.
         :arg string client_cert: Filename for client SSL certificate, if any.
            See note below when used with ``curl_httpclient``.
+        :arg SSLContext ssl_options: `ssl.SSLContext` object for use in
+           ``simple_httpclient`` (unsupported by ``curl_httpclient``).
+           Overrides ``validate_cert``, ``ca_certs``, ``client_key``,
+           and ``client_cert``.
+        :arg bool allow_ipv6: Use IPv6 when available?  Default is false in
+           ``simple_httpclient`` and true in ``curl_httpclient``
         :arg bool expect_100_continue: If true, send the
            ``Expect: 100-continue`` header and wait for a continue response
            before sending the request body.  Only supported with
@@ -408,6 +413,9 @@ class HTTPRequest(object):
 
         .. versionadded:: 4.0
            The ``body_producer`` and ``expect_100_continue`` arguments.
+
+        .. verisonadded:: 4.2
+           The ``ssl_options`` argument.
         """
         # Note that some of these attributes go through property setters
         # defined below.
@@ -445,6 +453,7 @@ class HTTPRequest(object):
         self.allow_ipv6 = allow_ipv6
         self.client_key = client_key
         self.client_cert = client_cert
+        self.ssl_options = ssl_options
         self.expect_100_continue = expect_100_continue
         self.start_time = time.time()
 
index 13400207a3d48934c5d01e197b235da25d301b22..ed9f7387e8b4e84f9bcf3f4c0836163c02d2561c 100644 (file)
@@ -220,6 +220,8 @@ class _HTTPConnection(httputil.HTTPMessageDelegate):
 
     def _get_ssl_options(self, scheme):
         if scheme == "https":
+            if self.request.ssl_options is not None:
+                return self.request.ssl_options
             ssl_options = {}
             if self.request.validate_cert:
                 ssl_options["cert_reqs"] = ssl.CERT_REQUIRED
index bb870db3b0308d109687ff78241b0c3edb7b67bb..a852168e1b602a3eb6f7ea95c140f09b7e7c1c08 100644 (file)
@@ -8,6 +8,7 @@ import logging
 import os
 import re
 import socket
+import ssl
 import sys
 
 from tornado import gen
@@ -432,6 +433,28 @@ class SimpleHTTPSClientTestCase(SimpleHTTPClientTestMixin, AsyncHTTPSTestCase):
                                      defaults=dict(validate_cert=False),
                                      **kwargs)
 
+    def test_ssl_options(self):
+        resp = self.fetch("/hello", ssl_options={})
+        self.assertEqual(resp.body, b"Hello world!")
+
+    def test_ssl_context(self):
+        resp = self.fetch("/hello",
+                          ssl_options=ssl.SSLContext(ssl.PROTOCOL_SSLv23))
+        self.assertEqual(resp.body, b"Hello world!")
+
+    def test_ssl_options_handshake_fail(self):
+        with ExpectLog(gen_log, "SSL Error|Uncaught exception"):
+            resp = self.fetch(
+                "/hello", ssl_options=dict(cert_reqs=ssl.CERT_REQUIRED))
+        self.assertRaises(ssl.SSLError, resp.rethrow)
+
+    def test_ssl_context_handshake_fail(self):
+        with ExpectLog(gen_log, "SSL Error|Uncaught exception"):
+            ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+            ctx.verify_mode = ssl.CERT_REQUIRED
+            resp = self.fetch("/hello", ssl_options=ctx)
+        self.assertRaises(ssl.SSLError, resp.rethrow)
+
 
 class CreateAsyncHTTPClientTestCase(AsyncTestCase):
     def setUp(self):