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