Skip to content

Commit 1538f24

Browse files
committed
Merge pull request #461 from plotly/https_stream
Https stream
2 parents 0a57c11 + 1eeec5a commit 1538f24

File tree

6 files changed

+227
-119
lines changed

6 files changed

+227
-119
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
44

55
## [Unreleased]
66

7+
## [1.9.12] - 2016-05-16
8+
### Added
9+
SSL support for streaming.
10+
711
## [1.9.11] - 2016-05-02
812
### Added
913
- The FigureFactory can now create scatter plot matrices with `.create_scatterplotmatrix`. Check it out with:

plotly/plotly/chunked_requests/chunked_request.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import os
44
from six.moves import http_client
55
from six.moves.urllib.parse import urlparse
6+
from ssl import SSLError
7+
68

79
class Stream:
8-
def __init__(self, server, port=80, headers={}, url='/'):
9-
''' Initialize a stream object and an HTTP Connection
10+
def __init__(self, server, port=80, headers={}, url='/', ssl_enabled=False):
11+
''' Initialize a stream object and an HTTP or HTTPS connection
1012
with chunked Transfer-Encoding to server:port with optional headers.
1113
'''
1214
self.maxtries = 5
@@ -17,6 +19,7 @@ def __init__(self, server, port=80, headers={}, url='/'):
1719
self._port = port
1820
self._headers = headers
1921
self._url = url
22+
self._ssl_enabled = ssl_enabled
2023
self._connect()
2124

2225
def write(self, data, reconnect_on=('', 200, )):
@@ -73,17 +76,21 @@ def write(self, data, reconnect_on=('', 200, )):
7376
def _get_proxy_config(self):
7477
"""
7578
Determine if self._url should be passed through a proxy. If so, return
76-
the appropriate proxy_server and proxy_port
79+
the appropriate proxy_server and proxy_port. Assumes https_proxy is used
80+
when ssl_enabled=True.
7781
7882
"""
7983

8084
proxy_server = None
8185
proxy_port = None
86+
ssl_enabled = self._ssl_enabled
8287

83-
## only doing HTTPConnection, so only use http_proxy
84-
proxy = os.environ.get("http_proxy")
88+
if ssl_enabled:
89+
proxy = os.environ.get("https_proxy")
90+
else:
91+
proxy = os.environ.get("http_proxy")
8592
no_proxy = os.environ.get("no_proxy")
86-
no_proxy_url = no_proxy and self._url in no_proxy
93+
no_proxy_url = no_proxy and self._server in no_proxy
8794

8895
if proxy and not no_proxy_url:
8996
p = urlparse(proxy)
@@ -93,19 +100,30 @@ def _get_proxy_config(self):
93100
return proxy_server, proxy_port
94101

95102
def _connect(self):
96-
''' Initialize an HTTP connection with chunked Transfer-Encoding
103+
''' Initialize an HTTP/HTTPS connection with chunked Transfer-Encoding
97104
to server:port with optional headers.
98105
'''
99106
server = self._server
100107
port = self._port
101108
headers = self._headers
109+
ssl_enabled = self._ssl_enabled
102110
proxy_server, proxy_port = self._get_proxy_config()
103111

104112
if (proxy_server and proxy_port):
105-
self._conn = http_client.HTTPConnection(proxy_server, proxy_port)
113+
if ssl_enabled:
114+
self._conn = http_client.HTTPSConnection(
115+
proxy_server, proxy_port
116+
)
117+
else:
118+
self._conn = http_client.HTTPConnection(
119+
proxy_server, proxy_port
120+
)
106121
self._conn.set_tunnel(server, port)
107122
else:
108-
self._conn = http_client.HTTPConnection(server, port)
123+
if ssl_enabled:
124+
self._conn = http_client.HTTPSConnection(server, port)
125+
else:
126+
self._conn = http_client.HTTPConnection(server, port)
109127

110128
self._conn.putrequest('POST', self._url)
111129
self._conn.putheader('Transfer-Encoding', 'chunked')
@@ -236,6 +254,16 @@ def _isconnected(self):
236254
# let's just assume that we're still connected and
237255
# hopefully recieve some data on the next try.
238256
return True
257+
elif isinstance(e, SSLError):
258+
if e.errno == 2:
259+
# errno 2 occurs when trying to read or write data, but more
260+
# data needs to be received on the underlying TCP transport
261+
# before the request can be fulfilled.
262+
#
263+
# Python 2.7.9+ and Python 3.3+ give this its own exception,
264+
# SSLWantReadError
265+
return True
266+
raise e
239267
else:
240268
# Unknown scenario
241269
raise e

plotly/plotly/plotly.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,9 @@ class Stream:
439439
440440
"""
441441

442+
HTTP_PORT = 80
443+
HTTPS_PORT = 443
444+
442445
@utils.template_doc(**tools.get_config_file())
443446
def __init__(self, stream_id):
444447
"""
@@ -454,6 +457,30 @@ def __init__(self, stream_id):
454457
self.connected = False
455458
self._stream = None
456459

460+
def get_streaming_specs(self):
461+
"""
462+
Returns the streaming server, port, ssl_enabled flag, and headers.
463+
464+
"""
465+
streaming_url = get_config()['plotly_streaming_domain']
466+
ssl_enabled = 'https' in streaming_url
467+
port = self.HTTPS_PORT if ssl_enabled else self.HTTP_PORT
468+
469+
# If no scheme (https/https) is included in the streaming_url, the
470+
# host will be None. Use streaming_url in this case.
471+
host = (six.moves.urllib.parse.urlparse(streaming_url).hostname or
472+
streaming_url)
473+
474+
headers = {'Host': host, 'plotly-streamtoken': self.stream_id}
475+
streaming_specs = {
476+
'server': host,
477+
'port': port,
478+
'ssl_enabled': ssl_enabled,
479+
'headers': headers
480+
}
481+
482+
return streaming_specs
483+
457484
def heartbeat(self, reconnect_on=(200, '', 408)):
458485
"""
459486
Keep stream alive. Streams will close after ~1 min of inactivity.
@@ -481,10 +508,8 @@ def open(self):
481508
https://plot.ly/python/streaming/
482509
483510
"""
484-
streaming_url = get_config()['plotly_streaming_domain']
485-
self._stream = chunked_requests.Stream(
486-
streaming_url, 80, {'Host': streaming_url,
487-
'plotly-streamtoken': self.stream_id})
511+
streaming_specs = self.get_streaming_specs()
512+
self._stream = chunked_requests.Stream(**streaming_specs)
488513

489514
def write(self, trace, layout=None, validate=True,
490515
reconnect_on=(200, '', 408)):

0 commit comments

Comments
 (0)