diff --git a/CHANGELOG.md b/CHANGELOG.md index 13c5fca8fbf..3bb5b3575bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [1.12.0] - 2016-06-06 +### Added +- Added ability to enable/disable SSL certificate verification for streaming. Disabling SSL certification verification requires Python v2.7.9 / v3.4.3 (or above). This feature can be toggled via the `plotly_ssl_verification` configuration setting. + ## [1.11.0] - 2016-05-27 ### Updated - Changed the default option for `create_distplot` in the figure factory from `probability` to `probability density` and also added the `histnorm` parameter to allow the user to choose between the two options. diff --git a/plotly/plotly/chunked_requests/chunked_request.py b/plotly/plotly/chunked_requests/chunked_request.py index c0be184a7a8..eb70488f59c 100644 --- a/plotly/plotly/chunked_requests/chunked_request.py +++ b/plotly/plotly/chunked_requests/chunked_request.py @@ -1,13 +1,15 @@ import time import six import os +import ssl + from six.moves import http_client from six.moves.urllib.parse import urlparse -from ssl import SSLError class Stream: - def __init__(self, server, port=80, headers={}, url='/', ssl_enabled=False): + def __init__(self, server, port=80, headers={}, url='/', ssl_enabled=False, + ssl_verification_enabled=True): ''' Initialize a stream object and an HTTP or HTTPS connection with chunked Transfer-Encoding to server:port with optional headers. ''' @@ -20,6 +22,7 @@ def __init__(self, server, port=80, headers={}, url='/', ssl_enabled=False): self._headers = headers self._url = url self._ssl_enabled = ssl_enabled + self._ssl_verification_enabled = ssl_verification_enabled self._connect() def write(self, data, reconnect_on=('', 200, )): @@ -99,6 +102,19 @@ def _get_proxy_config(self): return proxy_server, proxy_port + def _get_ssl_context(self): + """ + Return an unverified context if ssl verification is disabled. + + """ + + context = None + + if not self._ssl_verification_enabled: + context = ssl._create_unverified_context() + + return context + def _connect(self): ''' Initialize an HTTP/HTTPS connection with chunked Transfer-Encoding to server:port with optional headers. @@ -111,8 +127,9 @@ def _connect(self): if (proxy_server and proxy_port): if ssl_enabled: + context = self._get_ssl_context() self._conn = http_client.HTTPSConnection( - proxy_server, proxy_port + proxy_server, proxy_port, context=context ) else: self._conn = http_client.HTTPConnection( @@ -121,7 +138,10 @@ def _connect(self): self._conn.set_tunnel(server, port) else: if ssl_enabled: - self._conn = http_client.HTTPSConnection(server, port) + context = self._get_ssl_context() + self._conn = http_client.HTTPSConnection( + server, port, context=context + ) else: self._conn = http_client.HTTPConnection(server, port) @@ -254,7 +274,7 @@ def _isconnected(self): # let's just assume that we're still connected and # hopefully recieve some data on the next try. return True - elif isinstance(e, SSLError): + elif isinstance(e, ssl.SSLError): if e.errno == 2: # errno 2 occurs when trying to read or write data, but more # data needs to be received on the underlying TCP transport diff --git a/plotly/plotly/plotly.py b/plotly/plotly/plotly.py index 6e3c481a96a..a1069f8c506 100644 --- a/plotly/plotly/plotly.py +++ b/plotly/plotly/plotly.py @@ -463,6 +463,7 @@ def get_streaming_specs(self): """ streaming_url = get_config()['plotly_streaming_domain'] + ssl_verification_enabled = get_config()['plotly_ssl_verification'] ssl_enabled = 'https' in streaming_url port = self.HTTPS_PORT if ssl_enabled else self.HTTP_PORT @@ -476,6 +477,7 @@ def get_streaming_specs(self): 'server': host, 'port': port, 'ssl_enabled': ssl_enabled, + 'ssl_verification_enabled': ssl_verification_enabled, 'headers': headers } diff --git a/plotly/tests/test_core/test_stream/test_stream.py b/plotly/tests/test_core/test_stream/test_stream.py index 2e0a1131010..09b3c54a604 100644 --- a/plotly/tests/test_core/test_stream/test_stream.py +++ b/plotly/tests/test_core/test_stream/test_stream.py @@ -18,7 +18,8 @@ tk = 'vaia8trjjb' config = {'plotly_domain': 'https://plot.ly', 'plotly_streaming_domain': 'stream.plot.ly', - 'plotly_api_domain': 'https://api.plot.ly'} + 'plotly_api_domain': 'https://api.plot.ly', + 'plotly_ssl_verification': False} class TestStreaming(TestCase): @@ -127,6 +128,7 @@ def test_stream_no_scheme(self): 'server': 'stream.plot.ly', 'port': 80, 'ssl_enabled': False, + 'ssl_verification_enabled': False, 'headers': { 'Host': 'stream.plot.ly', 'plotly-streamtoken': tk @@ -147,6 +149,7 @@ def test_stream_http(self): 'server': 'stream.plot.ly', 'port': 80, 'ssl_enabled': False, + 'ssl_verification_enabled': False, 'headers': { 'Host': 'stream.plot.ly', 'plotly-streamtoken': tk @@ -158,15 +161,20 @@ def test_stream_http(self): def test_stream_https(self): # If the https scheme is used in the plotly_streaming_domain, port 443 - # should be used for streaming and ssl_enabled should be True + # should be used for streaming, ssl_enabled should be True, + # and ssl_verification_enabled should equal plotly_ssl_verification - py.sign_in(un, ak, - **{'plotly_streaming_domain': 'https://stream.plot.ly'}) + ssl_stream_config = { + 'plotly_streaming_domain': 'https://stream.plot.ly', + 'plotly_ssl_verification': True + } + py.sign_in(un, ak, **ssl_stream_config) my_stream = py.Stream(tk) expected_streaming_specs = { 'server': 'stream.plot.ly', 'port': 443, 'ssl_enabled': True, + 'ssl_verification_enabled': True, 'headers': { 'Host': 'stream.plot.ly', 'plotly-streamtoken': tk diff --git a/plotly/version.py b/plotly/version.py index da77e85c696..666b2f71cf9 100644 --- a/plotly/version.py +++ b/plotly/version.py @@ -1 +1 @@ -__version__ = '1.11.0' +__version__ = '1.12.0' diff --git a/submodules/chunked_requests b/submodules/chunked_requests index 42799319b8f..6d7c5ddc71c 160000 --- a/submodules/chunked_requests +++ b/submodules/chunked_requests @@ -1 +1 @@ -Subproject commit 42799319b8f06ce89a3831b3ff911207ca0b6a02 +Subproject commit 6d7c5ddc71cebc343c22a16f2151b11c68720a1d