1 """ndg_httpsclient HTTPS module containing PyOpenSSL implementation of
2 httplib.HTTPSConnection
3
4 PyOpenSSL utility to make a httplib-like interface suitable for use with
5 urllib2
6 """
7 __author__ = "P J Kershaw (STFC)"
8 __date__ = "09/12/11"
9 __copyright__ = "(C) 2012 Science and Technology Facilities Council"
10 __license__ = "BSD - see LICENSE file in top-level directory"
11 __contact__ = "Philip.Kershaw@stfc.ac.uk"
12 __revision__ = '$Id$'
13 import logging
14 import socket
15 import sys
16
17 if sys.version_info[0] > 2:
18 from http.client import HTTPS_PORT
19 from http.client import HTTPConnection
20
21 from urllib.request import AbstractHTTPHandler
22 else:
23 from httplib import HTTPS_PORT
24 from httplib import HTTPConnection
25
26 from urllib2 import AbstractHTTPHandler
27
28
29 from OpenSSL import SSL
30
31 from ndg.httpsclient.ssl_socket import SSLSocket
32
33 log = logging.getLogger(__name__)
34
35
37 """This class allows communication via SSL using PyOpenSSL.
38 It is based on httplib.HTTPSConnection, modified to use PyOpenSSL.
39
40 Note: This uses the constructor inherited from HTTPConnection to allow it to
41 be used with httplib and HTTPSContextHandler. To use the class directly with
42 an SSL context set ssl_context after construction.
43
44 @cvar default_port: default port for this class (443)
45 @type default_port: int
46 @cvar default_ssl_method: default SSL method used if no SSL context is
47 explicitly set - defaults to version 2/3.
48 @type default_ssl_method: int
49 """
50 default_port = HTTPS_PORT
51 default_ssl_method = SSL.SSLv23_METHOD
52
53 - def __init__(self, host, port=None, strict=None,
54 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, ssl_context=None):
55 HTTPConnection.__init__(self, host, port, strict, timeout)
56 if not hasattr(self, 'ssl_context'):
57 self.ssl_context = None
58
59 if ssl_context is not None:
60 if not isinstance(ssl_context, SSL.Context):
61 raise TypeError('Expecting OpenSSL.SSL.Context type for "'
62 'ssl_context" keyword; got %r instead' %
63 ssl_context)
64
65 self.ssl_context = ssl_context
66
68 """Create SSL socket and connect to peer
69 """
70 if getattr(self, 'ssl_context', None):
71 if not isinstance(self.ssl_context, SSL.Context):
72 raise TypeError('Expecting OpenSSL.SSL.Context type for "'
73 'ssl_context" attribute; got %r instead' %
74 self.ssl_context)
75 ssl_context = self.ssl_context
76 else:
77 ssl_context = SSL.Context(self.__class__.default_ssl_method)
78
79 sock = socket.create_connection((self.host, self.port), self.timeout)
80
81
82 if getattr(self, '_tunnel_host', None):
83 self.sock = sock
84 self._tunnel()
85
86 self.sock = SSLSocket(ssl_context, sock)
87
88
89 self.sock.set_connect_state()
90
92 """Close socket and shut down SSL connection"""
93 if hasattr(self.sock, "close"):
94 self.sock.close()
95
96
97 -class HTTPSContextHandler(AbstractHTTPHandler):
98 '''HTTPS handler that allows a SSL context to be set for the SSL
99 connections.
100 '''
101 https_request = AbstractHTTPHandler.do_request_
102
103 - def __init__(self, ssl_context, debuglevel=0):
104 """
105 @param ssl_context:SSL context
106 @type ssl_context: OpenSSL.SSL.Context
107 @param debuglevel: debug level for HTTPSHandler
108 @type debuglevel: int
109 """
110 AbstractHTTPHandler.__init__(self, debuglevel)
111
112 if ssl_context is not None:
113 if not isinstance(ssl_context, SSL.Context):
114 raise TypeError('Expecting OpenSSL.SSL.Context type for "'
115 'ssl_context" keyword; got %r instead' %
116 ssl_context)
117 self.ssl_context = ssl_context
118 else:
119 self.ssl_context = SSL.Context(SSL.TLSv1_METHOD)
120
121 - def https_open(self, req):
122 """Opens HTTPS request
123 @param req: HTTP request
124 @return: HTTP Response object
125 """
126
127
128 customHTTPSContextConnection = type('CustomHTTPSContextConnection',
129 (HTTPSConnection, object),
130 {'ssl_context': self.ssl_context})
131 return self.do_open(customHTTPSContextConnection, req)
132