We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
assets fetch: handshake timeout it is not being caught
During investigation of #4824 I found a problem with timeout during handshake.


```
Process Process-1:
Traceback (most recent call last):
  File "/usr/lib64/python3.7/urllib/request.py", line 1350, in do_open
    encode_chunked=req.has_header('Transfer-encoding'))
  File "/usr/lib64/python3.7/http/client.py", line 1277, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/lib64/python3.7/http/client.py", line 1323, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/lib64/python3.7/http/client.py", line 1272, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/lib64/python3.7/http/client.py", line 1032, in _send_output
    self.send(msg)
  File "/usr/lib64/python3.7/http/client.py", line 972, in send
    self.connect()
  File "/usr/lib64/python3.7/http/client.py", line 1447, in connect
    server_hostname=server_hostname)
  File "/usr/lib64/python3.7/ssl.py", line 423, in wrap_socket
    session=session
  File "/usr/lib64/python3.7/ssl.py", line 870, in _create
    self.do_handshake()
  File "/usr/lib64/python3.7/ssl.py", line 1139, in do_handshake
    self._sslobj.do_handshake()
socket.timeout: _ssl.c:1074: The handshake operation timed out

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib64/python3.7/multiprocessing/process.py", line 297, in _bootstrap
    self.run()
  File "/usr/lib64/python3.7/multiprocessing/process.py", line 99, in run
    self._target(*self._args, **self._kwargs)
  File "/home/local/src/beraldo/avocado/avocado/utils/download.py", line 69, in download
    src_file = url_open(url, data=data)
  File "/home/local/src/beraldo/avocado/avocado/utils/download.py", line 46, in url_open
    result = urlopen(url, data=data, timeout=timeout)
  File "/usr/lib64/python3.7/urllib/request.py", line 222, in urlopen
    return opener.open(url, data, timeout)
  File "/usr/lib64/python3.7/urllib/request.py", line 525, in open
    response = self._open(req, data)
  File "/usr/lib64/python3.7/urllib/request.py", line 543, in _open
    '_open', req)
  File "/usr/lib64/python3.7/urllib/request.py", line 503, in _call_chain
    result = func(*args)
  File "/usr/lib64/python3.7/urllib/request.py", line 1393, in https_open
    context=self._context, check_hostname=self._check_hostname)
  File "/usr/lib64/python3.7/urllib/request.py", line 1352, in do_open
    raise URLError(err)
urllib.error.URLError: <urlopen error _ssl.c:1074: The handshake operation timed out>
Failed to fetch download ([Errno 2] No such file or directory: '/home/local/avocado/data/cache/by_location/4135b9170bbc2afc3ec7f79c169bf722b4d2afe7/download.cd7fe814-e6c0-4b10-82cd-e390d92fa099').
(avocado-py3.7) [local@laptop avocado]$ 
```

Probably this is related to 9b75f9d62faf53248fc687bd6e4dde85bb705b56, but I'm still investigating.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `avocado/utils/download.py`
Content:
```
1 # This program is free software; you can redistribute it and/or modify
2 # it under the terms of the GNU General Public License as published by
3 # the Free Software Foundation; specifically version 2 of the License.
4 #
5 # This program is distributed in the hope that it will be useful,
6 # but WITHOUT ANY WARRANTY; without even the implied warranty of
7 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8 #
9 # See LICENSE for more details.
10 #
11 # This code was inspired in the autotest project,
12 # client/shared/utils.py
13 # Authors: Martin J Bligh <mbligh@google.com>, Andy Whitcroft <apw@shadowen.org>
14 
15 
16 """
17 Methods to download URLs and regular files.
18 """
19 
20 import logging
21 import os
22 import shutil
23 from multiprocessing import Process
24 from urllib.request import urlopen
25 
26 from . import aurl, crypto, output
27 
28 log = logging.getLogger('avocado.test')
29 
30 
31 def url_open(url, data=None, timeout=5):
32     """
33     Wrapper to :func:`urllib2.urlopen` with timeout addition.
34 
35     :param url: URL to open.
36     :param data: (optional) data to post.
37     :param timeout: (optional) default timeout in seconds. Please, be aware
38                     that timeout here is just for blocking operations during
39                     the connection setup, since this method doesn't read the
40                     file from the url.
41     :return: file-like object.
42     :raises: `URLError`.
43     """
44     result = urlopen(url, data=data, timeout=timeout)
45     msg = ('Retrieved URL "%s": content-length %s, date: "%s", '
46            'last-modified: "%s"')
47     log.debug(msg, url,
48               result.headers.get('Content-Length', 'UNKNOWN'),
49               result.headers.get('Date', 'UNKNOWN'),
50               result.headers.get('Last-Modified', 'UNKNOWN'))
51     return result
52 
53 
54 def url_download(url, filename, data=None, timeout=300):
55     """
56     Retrieve a file from given url.
57 
58     :param url: source URL.
59     :param filename: destination path.
60     :param data: (optional) data to post.
61     :param timeout: (optional) default timeout in seconds.
62     :return: `None`.
63     """
64     def download():
65         log.info('Fetching %s -> %s', url, filename)
66 
67         src_file = url_open(url, data=data)
68         try:
69             with open(filename, 'wb') as dest_file:
70                 shutil.copyfileobj(src_file, dest_file)
71         finally:
72             src_file.close()
73 
74     process = Process(target=download)
75     process.start()
76     process.join(timeout)
77     if process.is_alive():
78         process.terminate()
79         process.join()
80         raise OSError("Aborting downloading. Timeout was reach.")
81 
82 
83 def url_download_interactive(url, output_file, title='', chunk_size=102400):
84     """
85     Interactively downloads a given file url to a given output file.
86 
87     :type url: string
88     :param url: URL for the file to be download
89     :type output_file: string
90     :param output_file: file name or absolute path on which to save the file to
91     :type title: string
92     :param title: optional title to go along the progress bar
93     :type chunk_size: integer
94     :param chunk_size: amount of data to read at a time
95     """
96     output_dir = os.path.dirname(output_file)
97     with open(output_file, 'w+b') as open_output_file:
98         input_file = urlopen(url)
99 
100         try:
101             file_size = int(input_file.headers['Content-Length'])
102         except KeyError:
103             raise ValueError('Could not find file size in HTTP headers')
104 
105         log.info('Downloading %s, %s to %s', os.path.basename(url),
106                  output.display_data_size(file_size), output_dir)
107 
108         progress_bar = output.ProgressBar(maximum=file_size, title=title)
109 
110         # Download the file, while interactively updating the progress
111         progress_bar.draw()
112         while True:
113             data = input_file.read(chunk_size)
114             if data:
115                 progress_bar.append_amount(len(data))
116                 open_output_file.write(data)
117             else:
118                 progress_bar.update_amount(file_size)
119                 break
120 
121 
122 def _get_file(src, dst, permissions=None):
123     if src == dst:
124         return
125 
126     if aurl.is_url(src):
127         url_download(src, dst)
128     else:
129         shutil.copyfile(src, dst)
130 
131     if permissions:
132         os.chmod(dst, permissions)
133     return dst
134 
135 
136 def get_file(src, dst, permissions=None, hash_expected=None,
137              hash_algorithm="md5", download_retries=1):
138     """
139     Gets a file from a source location, optionally using caching.
140 
141     If no hash_expected is provided, simply download the file. Else,
142     keep trying to download the file until download_failures exceeds
143     download_retries or the hashes match.
144 
145     If the hashes match, return dst. If download_failures exceeds
146     download_retries, raise an EnvironmentError.
147 
148     :param src: source path or URL. May be local or a remote file.
149     :param dst: destination path.
150     :param permissions: (optional) set access permissions.
151     :param hash_expected: Hash string that we expect the file downloaded to
152             have.
153     :param hash_algorithm: Algorithm used to calculate the hash string
154             (md5, sha1).
155     :param download_retries: Number of times we are going to retry a failed
156             download.
157     :raise: EnvironmentError.
158     :return: destination path.
159     """
160     def _verify_hash(filename):
161         if os.path.isfile(filename):
162             return crypto.hash_file(filename, algorithm=hash_algorithm)
163         return None
164 
165     if hash_expected is None:
166         return _get_file(src, dst, permissions)
167 
168     download_failures = 0
169     hash_file = _verify_hash(dst)
170 
171     while not hash_file == hash_expected:
172         hash_file = _verify_hash(_get_file(src, dst, permissions))
173         if hash_file != hash_expected:
174             log.error("It seems that dst %s is corrupted", dst)
175             download_failures += 1
176         if download_failures > download_retries:
177             raise EnvironmentError("Failed to retrieve %s. "
178                                    "Possible reasons - Network connectivity "
179                                    "problems or incorrect hash_expected "
180                                    "provided -> '%s'" %
181                                    (src, hash_expected))
182         else:
183             log.error("Retrying download of src %s", src)
184 
185     return dst
186
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/avocado/utils/download.py b/avocado/utils/download.py
--- a/avocado/utils/download.py
+++ b/avocado/utils/download.py
@@ -20,12 +20,14 @@
 import logging
 import os
 import shutil
+import socket
+import sys
 from multiprocessing import Process
 from urllib.request import urlopen
 
 from . import aurl, crypto, output
 
-log = logging.getLogger('avocado.test')
+log = logging.getLogger('avocado.utils.download')
 
 
 def url_open(url, data=None, timeout=5):
@@ -41,7 +43,13 @@
     :return: file-like object.
     :raises: `URLError`.
     """
-    result = urlopen(url, data=data, timeout=timeout)
+    try:
+        result = urlopen(url, data=data, timeout=timeout)
+    except socket.timeout as ex:
+        msg = "Timeout was reach: {}".format(str(ex))
+        log.error(msg)
+        return None
+
     msg = ('Retrieved URL "%s": content-length %s, date: "%s", '
            'last-modified: "%s"')
     log.debug(msg, url,
@@ -62,9 +70,13 @@
     :return: `None`.
     """
     def download():
-        log.info('Fetching %s -> %s', url, filename)
-
         src_file = url_open(url, data=data)
+        if not src_file:
+            msg = ("Failed to get file. Probably timeout was reach when "
+                   "connecting to the server.\n")
+            sys.stderr.write(msg)
+            sys.exit(1)
+
         try:
             with open(filename, 'wb') as dest_file:
                 shutil.copyfileobj(src_file, dest_file)
@@ -72,6 +84,7 @@
             src_file.close()
 
     process = Process(target=download)
+    log.info('Fetching %s -> %s', url, filename)
     process.start()
     process.join(timeout)
     if process.is_alive():

{"golden_diff": "diff --git a/avocado/utils/download.py b/avocado/utils/download.py\n--- a/avocado/utils/download.py\n+++ b/avocado/utils/download.py\n@@ -20,12 +20,14 @@\n import logging\n import os\n import shutil\n+import socket\n+import sys\n from multiprocessing import Process\n from urllib.request import urlopen\n \n from . import aurl, crypto, output\n \n-log = logging.getLogger('avocado.test')\n+log = logging.getLogger('avocado.utils.download')\n \n \n def url_open(url, data=None, timeout=5):\n@@ -41,7 +43,13 @@\n     :return: file-like object.\n     :raises: `URLError`.\n     \"\"\"\n-    result = urlopen(url, data=data, timeout=timeout)\n+    try:\n+        result = urlopen(url, data=data, timeout=timeout)\n+    except socket.timeout as ex:\n+        msg = \"Timeout was reach: {}\".format(str(ex))\n+        log.error(msg)\n+        return None\n+\n     msg = ('Retrieved URL \"%s\": content-length %s, date: \"%s\", '\n            'last-modified: \"%s\"')\n     log.debug(msg, url,\n@@ -62,9 +70,13 @@\n     :return: `None`.\n     \"\"\"\n     def download():\n-        log.info('Fetching %s -> %s', url, filename)\n-\n         src_file = url_open(url, data=data)\n+        if not src_file:\n+            msg = (\"Failed to get file. Probably timeout was reach when \"\n+                   \"connecting to the server.\\n\")\n+            sys.stderr.write(msg)\n+            sys.exit(1)\n+\n         try:\n             with open(filename, 'wb') as dest_file:\n                 shutil.copyfileobj(src_file, dest_file)\n@@ -72,6 +84,7 @@\n             src_file.close()\n \n     process = Process(target=download)\n+    log.info('Fetching %s -> %s', url, filename)\n     process.start()\n     process.join(timeout)\n     if process.is_alive():\n", "issue": "assets fetch: handshake timeout it is not being caught\nDuring investigation of #4824 I found a problem with timeout during handshake.\r\n\r\n\r\n```\r\nProcess Process-1:\r\nTraceback (most recent call last):\r\n  File \"/usr/lib64/python3.7/urllib/request.py\", line 1350, in do_open\r\n    encode_chunked=req.has_header('Transfer-encoding'))\r\n  File \"/usr/lib64/python3.7/http/client.py\", line 1277, in request\r\n    self._send_request(method, url, body, headers, encode_chunked)\r\n  File \"/usr/lib64/python3.7/http/client.py\", line 1323, in _send_request\r\n    self.endheaders(body, encode_chunked=encode_chunked)\r\n  File \"/usr/lib64/python3.7/http/client.py\", line 1272, in endheaders\r\n    self._send_output(message_body, encode_chunked=encode_chunked)\r\n  File \"/usr/lib64/python3.7/http/client.py\", line 1032, in _send_output\r\n    self.send(msg)\r\n  File \"/usr/lib64/python3.7/http/client.py\", line 972, in send\r\n    self.connect()\r\n  File \"/usr/lib64/python3.7/http/client.py\", line 1447, in connect\r\n    server_hostname=server_hostname)\r\n  File \"/usr/lib64/python3.7/ssl.py\", line 423, in wrap_socket\r\n    session=session\r\n  File \"/usr/lib64/python3.7/ssl.py\", line 870, in _create\r\n    self.do_handshake()\r\n  File \"/usr/lib64/python3.7/ssl.py\", line 1139, in do_handshake\r\n    self._sslobj.do_handshake()\r\nsocket.timeout: _ssl.c:1074: The handshake operation timed out\r\n\r\nDuring handling of the above exception, another exception occurred:\r\n\r\nTraceback (most recent call last):\r\n  File \"/usr/lib64/python3.7/multiprocessing/process.py\", line 297, in _bootstrap\r\n    self.run()\r\n  File \"/usr/lib64/python3.7/multiprocessing/process.py\", line 99, in run\r\n    self._target(*self._args, **self._kwargs)\r\n  File \"/home/local/src/beraldo/avocado/avocado/utils/download.py\", line 69, in download\r\n    src_file = url_open(url, data=data)\r\n  File \"/home/local/src/beraldo/avocado/avocado/utils/download.py\", line 46, in url_open\r\n    result = urlopen(url, data=data, timeout=timeout)\r\n  File \"/usr/lib64/python3.7/urllib/request.py\", line 222, in urlopen\r\n    return opener.open(url, data, timeout)\r\n  File \"/usr/lib64/python3.7/urllib/request.py\", line 525, in open\r\n    response = self._open(req, data)\r\n  File \"/usr/lib64/python3.7/urllib/request.py\", line 543, in _open\r\n    '_open', req)\r\n  File \"/usr/lib64/python3.7/urllib/request.py\", line 503, in _call_chain\r\n    result = func(*args)\r\n  File \"/usr/lib64/python3.7/urllib/request.py\", line 1393, in https_open\r\n    context=self._context, check_hostname=self._check_hostname)\r\n  File \"/usr/lib64/python3.7/urllib/request.py\", line 1352, in do_open\r\n    raise URLError(err)\r\nurllib.error.URLError: <urlopen error _ssl.c:1074: The handshake operation timed out>\r\nFailed to fetch download ([Errno 2] No such file or directory: '/home/local/avocado/data/cache/by_location/4135b9170bbc2afc3ec7f79c169bf722b4d2afe7/download.cd7fe814-e6c0-4b10-82cd-e390d92fa099').\r\n(avocado-py3.7) [local@laptop avocado]$ \r\n```\r\n\r\nProbably this is related to 9b75f9d62faf53248fc687bd6e4dde85bb705b56, but I'm still investigating.\n", "before_files": [{"content": "# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; specifically version 2 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n#\n# See LICENSE for more details.\n#\n# This code was inspired in the autotest project,\n# client/shared/utils.py\n# Authors: Martin J Bligh <mbligh@google.com>, Andy Whitcroft <apw@shadowen.org>\n\n\n\"\"\"\nMethods to download URLs and regular files.\n\"\"\"\n\nimport logging\nimport os\nimport shutil\nfrom multiprocessing import Process\nfrom urllib.request import urlopen\n\nfrom . import aurl, crypto, output\n\nlog = logging.getLogger('avocado.test')\n\n\ndef url_open(url, data=None, timeout=5):\n    \"\"\"\n    Wrapper to :func:`urllib2.urlopen` with timeout addition.\n\n    :param url: URL to open.\n    :param data: (optional) data to post.\n    :param timeout: (optional) default timeout in seconds. Please, be aware\n                    that timeout here is just for blocking operations during\n                    the connection setup, since this method doesn't read the\n                    file from the url.\n    :return: file-like object.\n    :raises: `URLError`.\n    \"\"\"\n    result = urlopen(url, data=data, timeout=timeout)\n    msg = ('Retrieved URL \"%s\": content-length %s, date: \"%s\", '\n           'last-modified: \"%s\"')\n    log.debug(msg, url,\n              result.headers.get('Content-Length', 'UNKNOWN'),\n              result.headers.get('Date', 'UNKNOWN'),\n              result.headers.get('Last-Modified', 'UNKNOWN'))\n    return result\n\n\ndef url_download(url, filename, data=None, timeout=300):\n    \"\"\"\n    Retrieve a file from given url.\n\n    :param url: source URL.\n    :param filename: destination path.\n    :param data: (optional) data to post.\n    :param timeout: (optional) default timeout in seconds.\n    :return: `None`.\n    \"\"\"\n    def download():\n        log.info('Fetching %s -> %s', url, filename)\n\n        src_file = url_open(url, data=data)\n        try:\n            with open(filename, 'wb') as dest_file:\n                shutil.copyfileobj(src_file, dest_file)\n        finally:\n            src_file.close()\n\n    process = Process(target=download)\n    process.start()\n    process.join(timeout)\n    if process.is_alive():\n        process.terminate()\n        process.join()\n        raise OSError(\"Aborting downloading. Timeout was reach.\")\n\n\ndef url_download_interactive(url, output_file, title='', chunk_size=102400):\n    \"\"\"\n    Interactively downloads a given file url to a given output file.\n\n    :type url: string\n    :param url: URL for the file to be download\n    :type output_file: string\n    :param output_file: file name or absolute path on which to save the file to\n    :type title: string\n    :param title: optional title to go along the progress bar\n    :type chunk_size: integer\n    :param chunk_size: amount of data to read at a time\n    \"\"\"\n    output_dir = os.path.dirname(output_file)\n    with open(output_file, 'w+b') as open_output_file:\n        input_file = urlopen(url)\n\n        try:\n            file_size = int(input_file.headers['Content-Length'])\n        except KeyError:\n            raise ValueError('Could not find file size in HTTP headers')\n\n        log.info('Downloading %s, %s to %s', os.path.basename(url),\n                 output.display_data_size(file_size), output_dir)\n\n        progress_bar = output.ProgressBar(maximum=file_size, title=title)\n\n        # Download the file, while interactively updating the progress\n        progress_bar.draw()\n        while True:\n            data = input_file.read(chunk_size)\n            if data:\n                progress_bar.append_amount(len(data))\n                open_output_file.write(data)\n            else:\n                progress_bar.update_amount(file_size)\n                break\n\n\ndef _get_file(src, dst, permissions=None):\n    if src == dst:\n        return\n\n    if aurl.is_url(src):\n        url_download(src, dst)\n    else:\n        shutil.copyfile(src, dst)\n\n    if permissions:\n        os.chmod(dst, permissions)\n    return dst\n\n\ndef get_file(src, dst, permissions=None, hash_expected=None,\n             hash_algorithm=\"md5\", download_retries=1):\n    \"\"\"\n    Gets a file from a source location, optionally using caching.\n\n    If no hash_expected is provided, simply download the file. Else,\n    keep trying to download the file until download_failures exceeds\n    download_retries or the hashes match.\n\n    If the hashes match, return dst. If download_failures exceeds\n    download_retries, raise an EnvironmentError.\n\n    :param src: source path or URL. May be local or a remote file.\n    :param dst: destination path.\n    :param permissions: (optional) set access permissions.\n    :param hash_expected: Hash string that we expect the file downloaded to\n            have.\n    :param hash_algorithm: Algorithm used to calculate the hash string\n            (md5, sha1).\n    :param download_retries: Number of times we are going to retry a failed\n            download.\n    :raise: EnvironmentError.\n    :return: destination path.\n    \"\"\"\n    def _verify_hash(filename):\n        if os.path.isfile(filename):\n            return crypto.hash_file(filename, algorithm=hash_algorithm)\n        return None\n\n    if hash_expected is None:\n        return _get_file(src, dst, permissions)\n\n    download_failures = 0\n    hash_file = _verify_hash(dst)\n\n    while not hash_file == hash_expected:\n        hash_file = _verify_hash(_get_file(src, dst, permissions))\n        if hash_file != hash_expected:\n            log.error(\"It seems that dst %s is corrupted\", dst)\n            download_failures += 1\n        if download_failures > download_retries:\n            raise EnvironmentError(\"Failed to retrieve %s. \"\n                                   \"Possible reasons - Network connectivity \"\n                                   \"problems or incorrect hash_expected \"\n                                   \"provided -> '%s'\" %\n                                   (src, hash_expected))\n        else:\n            log.error(\"Retrying download of src %s\", src)\n\n    return dst\n", "path": "avocado/utils/download.py"}], "after_files": [{"content": "# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; specifically version 2 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n#\n# See LICENSE for more details.\n#\n# This code was inspired in the autotest project,\n# client/shared/utils.py\n# Authors: Martin J Bligh <mbligh@google.com>, Andy Whitcroft <apw@shadowen.org>\n\n\n\"\"\"\nMethods to download URLs and regular files.\n\"\"\"\n\nimport logging\nimport os\nimport shutil\nimport socket\nimport sys\nfrom multiprocessing import Process\nfrom urllib.request import urlopen\n\nfrom . import aurl, crypto, output\n\nlog = logging.getLogger('avocado.utils.download')\n\n\ndef url_open(url, data=None, timeout=5):\n    \"\"\"\n    Wrapper to :func:`urllib2.urlopen` with timeout addition.\n\n    :param url: URL to open.\n    :param data: (optional) data to post.\n    :param timeout: (optional) default timeout in seconds. Please, be aware\n                    that timeout here is just for blocking operations during\n                    the connection setup, since this method doesn't read the\n                    file from the url.\n    :return: file-like object.\n    :raises: `URLError`.\n    \"\"\"\n    try:\n        result = urlopen(url, data=data, timeout=timeout)\n    except socket.timeout as ex:\n        msg = \"Timeout was reach: {}\".format(str(ex))\n        log.error(msg)\n        return None\n\n    msg = ('Retrieved URL \"%s\": content-length %s, date: \"%s\", '\n           'last-modified: \"%s\"')\n    log.debug(msg, url,\n              result.headers.get('Content-Length', 'UNKNOWN'),\n              result.headers.get('Date', 'UNKNOWN'),\n              result.headers.get('Last-Modified', 'UNKNOWN'))\n    return result\n\n\ndef url_download(url, filename, data=None, timeout=300):\n    \"\"\"\n    Retrieve a file from given url.\n\n    :param url: source URL.\n    :param filename: destination path.\n    :param data: (optional) data to post.\n    :param timeout: (optional) default timeout in seconds.\n    :return: `None`.\n    \"\"\"\n    def download():\n        src_file = url_open(url, data=data)\n        if not src_file:\n            msg = (\"Failed to get file. Probably timeout was reach when \"\n                   \"connecting to the server.\\n\")\n            sys.stderr.write(msg)\n            sys.exit(1)\n\n        try:\n            with open(filename, 'wb') as dest_file:\n                shutil.copyfileobj(src_file, dest_file)\n        finally:\n            src_file.close()\n\n    process = Process(target=download)\n    log.info('Fetching %s -> %s', url, filename)\n    process.start()\n    process.join(timeout)\n    if process.is_alive():\n        process.terminate()\n        process.join()\n        raise OSError(\"Aborting downloading. Timeout was reach.\")\n\n\ndef url_download_interactive(url, output_file, title='', chunk_size=102400):\n    \"\"\"\n    Interactively downloads a given file url to a given output file.\n\n    :type url: string\n    :param url: URL for the file to be download\n    :type output_file: string\n    :param output_file: file name or absolute path on which to save the file to\n    :type title: string\n    :param title: optional title to go along the progress bar\n    :type chunk_size: integer\n    :param chunk_size: amount of data to read at a time\n    \"\"\"\n    output_dir = os.path.dirname(output_file)\n    with open(output_file, 'w+b') as open_output_file:\n        input_file = urlopen(url)\n\n        try:\n            file_size = int(input_file.headers['Content-Length'])\n        except KeyError:\n            raise ValueError('Could not find file size in HTTP headers')\n\n        log.info('Downloading %s, %s to %s', os.path.basename(url),\n                 output.display_data_size(file_size), output_dir)\n\n        progress_bar = output.ProgressBar(maximum=file_size, title=title)\n\n        # Download the file, while interactively updating the progress\n        progress_bar.draw()\n        while True:\n            data = input_file.read(chunk_size)\n            if data:\n                progress_bar.append_amount(len(data))\n                open_output_file.write(data)\n            else:\n                progress_bar.update_amount(file_size)\n                break\n\n\ndef _get_file(src, dst, permissions=None):\n    if src == dst:\n        return\n\n    if aurl.is_url(src):\n        url_download(src, dst)\n    else:\n        shutil.copyfile(src, dst)\n\n    if permissions:\n        os.chmod(dst, permissions)\n    return dst\n\n\ndef get_file(src, dst, permissions=None, hash_expected=None,\n             hash_algorithm=\"md5\", download_retries=1):\n    \"\"\"\n    Gets a file from a source location, optionally using caching.\n\n    If no hash_expected is provided, simply download the file. Else,\n    keep trying to download the file until download_failures exceeds\n    download_retries or the hashes match.\n\n    If the hashes match, return dst. If download_failures exceeds\n    download_retries, raise an EnvironmentError.\n\n    :param src: source path or URL. May be local or a remote file.\n    :param dst: destination path.\n    :param permissions: (optional) set access permissions.\n    :param hash_expected: Hash string that we expect the file downloaded to\n            have.\n    :param hash_algorithm: Algorithm used to calculate the hash string\n            (md5, sha1).\n    :param download_retries: Number of times we are going to retry a failed\n            download.\n    :raise: EnvironmentError.\n    :return: destination path.\n    \"\"\"\n    def _verify_hash(filename):\n        if os.path.isfile(filename):\n            return crypto.hash_file(filename, algorithm=hash_algorithm)\n        return None\n\n    if hash_expected is None:\n        return _get_file(src, dst, permissions)\n\n    download_failures = 0\n    hash_file = _verify_hash(dst)\n\n    while not hash_file == hash_expected:\n        hash_file = _verify_hash(_get_file(src, dst, permissions))\n        if hash_file != hash_expected:\n            log.error(\"It seems that dst %s is corrupted\", dst)\n            download_failures += 1\n        if download_failures > download_retries:\n            raise EnvironmentError(\"Failed to retrieve %s. \"\n                                   \"Possible reasons - Network connectivity \"\n                                   \"problems or incorrect hash_expected \"\n                                   \"provided -> '%s'\" %\n                                   (src, hash_expected))\n        else:\n            log.error(\"Retrying download of src %s\", src)\n\n    return dst\n", "path": "avocado/utils/download.py"}]}