Package zeroinstall :: Package injector :: Module _download_child
[frames] | no frames]

Source Code for Module zeroinstall.injector._download_child

  1  # Copyright (C) 2011, Thomas Leonard 
  2  # See the README file for details, or visit http://0install.net. 
  3   
  4  import sys, os, socket, ssl 
  5   
  6  from zeroinstall import _ 
  7  from zeroinstall.injector import download 
  8  from zeroinstall.support import ssl_match_hostname 
  9   
 10  if sys.version_info[0] > 2: 
 11          from urllib import request as urllib2 
 12          from http.client import HTTPSConnection, HTTPException 
 13  else: 
 14          import urllib2 
 15          from httplib import HTTPSConnection, HTTPException 
 16   
 17  try: 
 18          # http://pypi.python.org/pypi/certifi 
 19          import certifi 
 20          _fallback_ca_bundle = certifi.where() 
 21  except: 
 22          # Final fallback (last known signer of keylookup) 
 23          _fallback_ca_bundle = os.path.join(os.path.dirname(__file__), "EquifaxSecureCA.crt") 
 24   
 25  # Note: on MacOS X at least, it will also look in the system keychain provided that you supply *some* CAs. 
 26  # (if you don't specify any trusted CAs, Python trusts everything!) 
 27  # So, the "fallback" option doesn't necessarily mean that other sites won't work. 
 28  for ca_bundle in [ 
 29                  "/etc/ssl/certs/ca-certificates.crt",   # Debian/Ubuntu/Arch Linux 
 30                  "/etc/pki/tls/certs/ca-bundle.crt",     # Fedora/RHEL 
 31                  "/etc/ssl/ca-bundle.pem",               # openSUSE/SLE (claimed) 
 32                  "/var/lib/ca-certificates/ca-bundle.pem.new", # openSUSE (actual) 
 33                  _fallback_ca_bundle]: 
 34          if os.path.exists(ca_bundle): 
35 - class ValidatingHTTPSConnection(HTTPSConnection):
36 - def connect(self):
37 sock = socket.create_connection((self.host, self.port), self.timeout) 38 if hasattr(self, '_tunnel_host') and self._tunnel_host: 39 self.sock = sock 40 self._tunnel() 41 sock = ssl.wrap_socket(sock, cert_reqs = ssl.CERT_REQUIRED, ca_certs = ca_bundle) 42 ssl_match_hostname.match_hostname(sock.getpeercert(), self.host) 43 self.sock = sock
44
45 - class ValidatingHTTPSHandler(urllib2.HTTPSHandler):
46 - def https_open(self, req):
47 return self.do_open(self.getConnection, req)
48
49 - def getConnection(self, host, timeout=300):
50 """@type host: str""" 51 return ValidatingHTTPSConnection(host)
52 MyHTTPSHandler = ValidatingHTTPSHandler 53 break 54 else: 55 raise Exception("No root CA's found (not even the built-in one!); security of HTTPS connections cannot be verified") 56
57 -class Redirect(Exception):
58 - def __init__(self, req):
59 Exception.__init__(self, "Redirect") 60 self.req = req
61
62 -class MyRedirectHandler(urllib2.HTTPRedirectHandler):
63 """Throw an exception on redirects instead of continuing. The redirect will be handled in the main thread 64 so it can work with connection pooling."""
65 - def redirect_request(self, req, fp, code, msg, headers, newurl):
66 """@type code: int 67 @type msg: str 68 @type newurl: str""" 69 new_req = urllib2.HTTPRedirectHandler.redirect_request(self, req, fp, code, msg, headers, newurl) 70 if new_req: 71 raise Redirect(new_req)
72 73 # Our handler differs from the Python default in that: 74 # - we don't support file:// URLs 75 # - we don't follow HTTP redirects 76 _my_urlopen = urllib2.OpenerDirector() 77 for klass in [urllib2.ProxyHandler, urllib2.UnknownHandler, urllib2.HTTPHandler, 78 urllib2.HTTPDefaultErrorHandler, MyRedirectHandler, 79 urllib2.FTPHandler, urllib2.HTTPErrorProcessor, MyHTTPSHandler]: 80 _my_urlopen.add_handler(klass()) 81
82 -def download_in_thread(url, target_file, if_modified_since, notify_done):
83 """@type url: str 84 @type target_file: file""" 85 src = None 86 try: 87 #print "Child downloading", url 88 if url.startswith('http:') or url.startswith('https:') or url.startswith('ftp:'): 89 req = urllib2.Request(url) 90 if url.startswith('http:') and if_modified_since: 91 req.add_header('If-Modified-Since', if_modified_since) 92 src = _my_urlopen.open(req) 93 else: 94 raise Exception(_('Unsupported URL protocol in: %s') % url) 95 96 if sys.version_info[0] > 2: 97 sock_recv = src.fp.read1 # Python 3 98 else: 99 try: 100 sock_recv = src.fp._sock.recv # Python 2 101 except AttributeError: 102 sock_recv = src.fp.fp._sock.recv # Python 2.5 on FreeBSD 103 while True: 104 data = sock_recv(256) 105 if not data: break 106 target_file.write(data) 107 target_file.flush() 108 109 notify_done(download.RESULT_OK) 110 except (urllib2.HTTPError, urllib2.URLError, HTTPException, socket.error) as ex: 111 if isinstance(ex, urllib2.HTTPError) and ex.code == 304: # Not modified 112 notify_done(download.RESULT_NOT_MODIFIED) 113 else: 114 #print >>sys.stderr, "Error downloading '" + url + "': " + (str(ex) or str(ex.__class__.__name__)) 115 __, ex, tb = sys.exc_info() 116 notify_done(download.RESULT_FAILED, (download.DownloadError(_('Error downloading {url}: {ex}').format(url = url, ex = ex)), tb)) 117 except Redirect as ex: 118 notify_done(download.RESULT_REDIRECT, redirect = ex.req.get_full_url()) 119 except Exception as ex: 120 __, ex, tb = sys.exc_info() 121 notify_done(download.RESULT_FAILED, (ex, tb)) 122 finally: 123 if src is not None: 124 src.close()
125