1# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import hashlib
6import os
7import sys
8# when pylint runs the third_party module is the one from depot_tools
9# pylint: disable=E0611
10from third_party import fancy_urllib
11import urllib2
12
13
14SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
15
16
17def UrlOpen(url):
18  request = fancy_urllib.FancyRequest(url)
19  ca_certs = os.path.join(SCRIPT_DIR, 'cacerts.txt')
20  request.set_ssl_info(ca_certs=ca_certs)
21  url_opener = urllib2.build_opener(
22      fancy_urllib.FancyProxyHandler(),
23      fancy_urllib.FancyRedirectHandler(),
24      fancy_urllib.FancyHTTPSHandler())
25  return url_opener.open(request)
26
27
28def MakeProgressFunction(file_size):
29  # An inner function can only read nonlocal variables, not assign them. We can
30  # work around this by using a list of one element.
31  dots = [0]
32  def ShowKnownProgress(progress):
33    '''Returns a progress function based on a known file size'''
34    if progress == 0:
35      sys.stdout.write('|%s|\n' % ('=' * 48))
36    elif progress == -1:
37      sys.stdout.write('\n')
38    else:
39      new_dots = progress * 50 / file_size - dots[0]
40      sys.stdout.write('.' * new_dots)
41      dots[0] += new_dots
42    sys.stdout.flush()
43
44  return ShowKnownProgress
45
46
47def DownloadAndComputeHash(from_stream, to_stream=None, progress_func=None):
48  '''Read from from-stream and generate sha1 and size info.
49
50  Args:
51    from_stream:   An input stream that supports read.
52    to_stream:     [optional] the data is written to to_stream if it is
53                   provided.
54    progress_func: [optional] A function used to report download progress. If
55                   provided, progress_func is called with progress=0 at the
56                   beginning of the download, periodically with progress>0
57                   (== number of bytes read do far) during the download, and
58                   progress=-1 at the end or if the download was aborted.
59
60  Return
61    A tuple (sha1, size) where sha1 is a sha1-hash for the archive data and
62    size is the size of the archive data in bytes.'''
63  # Use a no-op progress function if none is specified.
64  def progress_no_op(progress):
65    pass
66  if not progress_func:
67    progress_func = progress_no_op
68
69  sha1_hash = hashlib.sha1()
70  size = 0
71  try:
72    progress_func(progress=0)
73    while True:
74      data = from_stream.read(32768)
75      if not data:
76        break
77      sha1_hash.update(data)
78      size += len(data)
79      if to_stream:
80        to_stream.write(data)
81      progress_func(size)
82  finally:
83    progress_func(progress=-1)
84  return sha1_hash.hexdigest(), size
85