File: https.py

package info (click to toggle)
ndg-httpsclient 0.5.1-8
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 248 kB
  • sloc: python: 1,083; makefile: 29; sh: 1
file content (133 lines) | stat: -rw-r--r-- 4,879 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""ndg_httpsclient HTTPS module containing PyOpenSSL implementation of
httplib.HTTPSConnection

PyOpenSSL utility to make a httplib-like interface suitable for use with 
urllib2
"""
__author__ = "P J Kershaw (STFC)"
__date__ = "09/12/11"
__copyright__ = "(C) 2012 Science and Technology Facilities Council"
__license__ = "BSD - see LICENSE file in top-level directory"
__contact__ = "Philip.Kershaw@stfc.ac.uk"
__revision__ = '$Id$'
import logging
import socket
import sys

if sys.version_info[0] > 2:
    from http.client import HTTPS_PORT
    from http.client import HTTPConnection

    from urllib.request import AbstractHTTPHandler
else:
    from httplib import HTTPS_PORT
    from httplib import HTTPConnection

    from urllib2 import AbstractHTTPHandler


from OpenSSL import SSL

from ndg.httpsclient.ssl_socket import SSLSocket

log = logging.getLogger(__name__)


class HTTPSConnection(HTTPConnection):
    """This class allows communication via SSL using PyOpenSSL.
    It is based on httplib.HTTPSConnection, modified to use PyOpenSSL.

    Note: This uses the constructor inherited from HTTPConnection to allow it to
    be used with httplib and HTTPSContextHandler. To use the class directly with
    an SSL context set ssl_context after construction.
    
    @cvar default_port: default port for this class (443)
    @type default_port: int
    @cvar default_ssl_method: default SSL method used if no SSL context is
    explicitly set - defaults to version 2/3.
    @type default_ssl_method: int
    """
    default_port = HTTPS_PORT
    default_ssl_method = SSL.TLSv1_2_METHOD
    
    def __init__(self, host, port=None, strict=None,
                 timeout=socket._GLOBAL_DEFAULT_TIMEOUT, ssl_context=None):
        HTTPConnection.__init__(self, host, port, strict, timeout)
        if not hasattr(self, 'ssl_context'):
            self.ssl_context = None

        if ssl_context is not None:
            if not isinstance(ssl_context, SSL.Context):
                raise TypeError('Expecting OpenSSL.SSL.Context type for "'
                                'ssl_context" keyword; got %r instead' %
                                ssl_context)
                
            self.ssl_context = ssl_context
            
    def connect(self):
        """Create SSL socket and connect to peer
        """
        if getattr(self, 'ssl_context', None):
            if not isinstance(self.ssl_context, SSL.Context):
                raise TypeError('Expecting OpenSSL.SSL.Context type for "'
                                'ssl_context" attribute; got %r instead' %
                                self.ssl_context)
            ssl_context = self.ssl_context
        else:
            ssl_context = SSL.Context(self.__class__.default_ssl_method)

        sock = socket.create_connection((self.host, self.port), self.timeout)
        
        # Tunnel if using a proxy - ONLY available for Python 2.6.2 and above
        if getattr(self, '_tunnel_host', None):
            self.sock = sock
            self._tunnel()
            
        self.sock = SSLSocket(ssl_context, sock)
        
        # Go to client mode.
        self.sock.set_connect_state()

    def close(self):
        """Close socket and shut down SSL connection"""
        if hasattr(self.sock, "close"):
            self.sock.close()
        
        
class HTTPSContextHandler(AbstractHTTPHandler):
    '''HTTPS handler that allows a SSL context to be set for the SSL
    connections.
    '''
    https_request = AbstractHTTPHandler.do_request_

    SSL_METHOD = SSL.TLSv1_2_METHOD
    
    def __init__(self, ssl_context, debuglevel=0):
        """
        @param ssl_context:SSL context
        @type ssl_context: OpenSSL.SSL.Context
        @param debuglevel: debug level for HTTPSHandler
        @type debuglevel: int
        """
        AbstractHTTPHandler.__init__(self, debuglevel)

        if ssl_context is not None:
            if not isinstance(ssl_context, SSL.Context):
                raise TypeError('Expecting OpenSSL.SSL.Context type for "'
                                'ssl_context" keyword; got %r instead' %
                                ssl_context)
            self.ssl_context = ssl_context
        else:
            self.ssl_context = SSL.Context(self.__class__.SSL_METHOD)

    def https_open(self, req):
        """Opens HTTPS request
        @param req: HTTP request
        @return: HTTP Response object
        """
        # Make a custom class extending HTTPSConnection, with the SSL context
        # set as a class variable so that it is available to the connect method.
        customHTTPSContextConnection = type('CustomHTTPSContextConnection',
                                            (HTTPSConnection, object),
                                            {'ssl_context': self.ssl_context})
        return self.do_open(customHTTPSContextConnection, req)