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
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
: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
.. 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.
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()
import os
import re
import socket
+import ssl
import sys
from tornado import gen
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):