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 from httplib import HTTPS_PORT
16 from httplib import HTTPConnection
17 from urllib2 import AbstractHTTPHandler
18
19
20 from OpenSSL import SSL
21
22 from ndg.httpsclient.ssl_socket import SSLSocket
23
24 log = logging.getLogger(__name__)
25
26
28 """This class allows communication via SSL using PyOpenSSL.
29 It is based on httplib.HTTPSConnection, modified to use PyOpenSSL.
30
31 Note: This uses the constructor inherited from HTTPConnection to allow it to
32 be used with httplib and HTTPSContextHandler. To use the class directly with
33 an SSL context set ssl_context after construction.
34
35 @cvar default_port: default port for this class (443)
36 @type default_port: int
37 @cvar default_ssl_method: default SSL method used if no SSL context is
38 explicitly set - defaults to version 2/3.
39 @type default_ssl_method: int
40 """
41 default_port = HTTPS_PORT
42 default_ssl_method = SSL.SSLv23_METHOD
43
44 - def __init__(self, host, port=None, strict=None,
45 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, ssl_context=None):
46 HTTPConnection.__init__(self, host, port, strict, timeout)
47 if not hasattr(self, 'ssl_context'):
48 self.ssl_context = None
49
50 if ssl_context is not None:
51 if not isinstance(ssl_context, SSL.Context):
52 raise TypeError('Expecting OpenSSL.SSL.Context type for "'
53 'ssl_context" keyword; got %r instead' %
54 ssl_context)
55
56 self.ssl_context = ssl_context
57
59 """Create SSL socket and connect to peer
60 """
61 if getattr(self, 'ssl_context', None):
62 if not isinstance(self.ssl_context, SSL.Context):
63 raise TypeError('Expecting OpenSSL.SSL.Context type for "'
64 'ssl_context" attribute; got %r instead' %
65 self.ssl_context)
66 ssl_context = self.ssl_context
67 else:
68 ssl_context = SSL.Context(self.__class__.default_ssl_method)
69
70 sock = socket.create_connection((self.host, self.port), self.timeout)
71
72
73 if getattr(self, '_tunnel_host', None):
74 self.sock = sock
75 self._tunnel()
76
77 self.sock = SSLSocket(ssl_context, sock)
78
79
80 self.sock.set_connect_state()
81
83 """Close socket and shut down SSL connection"""
84 self.sock.close()
85
86
87 -class HTTPSContextHandler(AbstractHTTPHandler):
88 '''HTTPS handler that allows a SSL context to be set for the SSL
89 connections.
90 '''
91 https_request = AbstractHTTPHandler.do_request_
92
93 - def __init__(self, ssl_context, debuglevel=0):
94 """
95 @param ssl_context:SSL context
96 @type ssl_context: OpenSSL.SSL.Context
97 @param debuglevel: debug level for HTTPSHandler
98 @type debuglevel: int
99 """
100 AbstractHTTPHandler.__init__(self, debuglevel)
101
102 if ssl_context is not None:
103 if not isinstance(ssl_context, SSL.Context):
104 raise TypeError('Expecting OpenSSL.SSL.Context type for "'
105 'ssl_context" keyword; got %r instead' %
106 ssl_context)
107 self.ssl_context = ssl_context
108 else:
109 self.ssl_context = SSL.Context(SSL.SSLv23_METHOD)
110
111 - def https_open(self, req):
112 """Opens HTTPS request
113 @param req: HTTP request
114 @return: HTTP Response object
115 """
116
117
118 customHTTPSContextConnection = type('CustomHTTPSContextConnection',
119 (HTTPSConnection, object),
120 {'ssl_context': self.ssl_context})
121 return self.do_open(customHTTPSContextConnection, req)
122