• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python2
2from __future__ import print_function
3import BaseHTTPServer
4import logging
5import os.path
6import ssl
7import sys
8import unittest
9
10import httplib2
11from httplib2.test import miniserver
12
13logger = logging.getLogger(__name__)
14
15
16class KeepAliveHandler(BaseHTTPServer.BaseHTTPRequestHandler):
17    """Request handler that keeps the HTTP connection open, so that the test can inspect the resulting SSL connection object
18
19    """
20
21    def do_GET(self):
22        self.send_response(200)
23        self.send_header("Content-Length", "0")
24        self.send_header("Connection", "keep-alive")
25        self.end_headers()
26
27        self.close_connection = 0
28
29    def log_message(self, s, *args):
30        # output via logging so nose can catch it
31        logger.info(s, *args)
32
33
34class HttpsContextTest(unittest.TestCase):
35    def setUp(self):
36        if sys.version_info < (2, 7, 9):
37            if hasattr(self, "skipTest"):
38                self.skipTest("SSLContext requires Python 2.7.9")
39            else:
40                return
41
42        self.ca_certs_path = os.path.join(os.path.dirname(__file__), "server.pem")
43        self.httpd, self.port = miniserver.start_server(KeepAliveHandler, True)
44
45    def tearDown(self):
46        self.httpd.shutdown()
47
48    def testHttpsContext(self):
49        client = httplib2.Http(ca_certs=self.ca_certs_path)
50
51        # Establish connection to local server
52        client.request("https://localhost:%d/" % (self.port))
53
54        # Verify that connection uses a TLS context with the correct hostname
55        conn = client.connections["https:localhost:%d" % self.port]
56
57        self.assertIsInstance(conn.sock, ssl.SSLSocket)
58        self.assertTrue(hasattr(conn.sock, "context"))
59        self.assertIsInstance(conn.sock.context, ssl.SSLContext)
60        self.assertTrue(conn.sock.context.check_hostname)
61        self.assertEqual(conn.sock.server_hostname, "localhost")
62        self.assertEqual(conn.sock.context.verify_mode, ssl.CERT_REQUIRED)
63        self.assertEqual(conn.sock.context.protocol, ssl.PROTOCOL_SSLv23)
64
65    def test_ssl_hostname_mismatch_repeat(self):
66        # https://github.com/httplib2/httplib2/issues/5
67
68        # FIXME(temoto): as of 2017-01-05 this is only a reference code, not useful test.
69        # Because it doesn't provoke described error on my machine.
70        # Instead `SSLContext.wrap_socket` raises `ssl.CertificateError`
71        # which was also added to original patch.
72
73        # url host is intentionally different, we provoke ssl hostname mismatch error
74        url = "https://127.0.0.1:%d/" % (self.port,)
75        http = httplib2.Http(ca_certs=self.ca_certs_path, proxy_info=None)
76
77        def once():
78            try:
79                http.request(url)
80                assert False, "expected certificate hostname mismatch error"
81            except Exception as e:
82                print("%s errno=%s" % (repr(e), getattr(e, "errno", None)))
83
84        once()
85        once()
86