Skip to content

Commit 72abfb0

Browse files
committed
remote: http: raise exception when download response with error status code
1 parent 510ff8a commit 72abfb0

File tree

5 files changed

+43
-5
lines changed

5 files changed

+43
-5
lines changed

dvc/exceptions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,3 +348,11 @@ def __init__(self, path, external_repo_path, external_repo_url):
348348
relpath(path, external_repo_path), external_repo_url
349349
)
350350
)
351+
352+
353+
class HTTPErrorStatusCodeException(DvcException):
354+
def __init__(self, code, reason):
355+
super(HTTPErrorStatusCodeException, self).__init__(
356+
"Server responded with error status code: '{}' and message: "
357+
"'{}'".format(code, reason)
358+
)

dvc/remote/http.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from dvc.config import Config
99
from dvc.config import ConfigError
1010
from dvc.exceptions import DvcException
11+
from dvc.exceptions import HTTPErrorStatusCodeException
1112
from dvc.progress import Tqdm
1213
from dvc.remote.base import RemoteBASE
1314
from dvc.scheme import Schemes
@@ -37,7 +38,11 @@ def __init__(self, repo, config):
3738
)
3839

3940
def _download(self, from_info, to_file, name=None, no_progress_bar=False):
40-
request = self._request("GET", from_info.url, stream=True)
41+
response = self._request("GET", from_info.url, stream=True)
42+
if response.status_code != 200:
43+
raise HTTPErrorStatusCodeException(
44+
response.status_code, response.reason
45+
)
4146
with Tqdm(
4247
total=None if no_progress_bar else self._content_length(from_info),
4348
leave=False,
@@ -46,7 +51,7 @@ def _download(self, from_info, to_file, name=None, no_progress_bar=False):
4651
disable=no_progress_bar,
4752
) as pbar:
4853
with open(to_file, "wb") as fd:
49-
for chunk in request.iter_content(chunk_size=self.CHUNK_SIZE):
54+
for chunk in response.iter_content(chunk_size=self.CHUNK_SIZE):
5055
fd.write(chunk)
5156
fd.flush()
5257
pbar.update(len(chunk))

tests/func/test_repro.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from tests.func.test_data_cloud import get_ssh_url
4444
from tests.func.test_data_cloud import TEST_AWS_REPO_BUCKET
4545
from tests.func.test_data_cloud import TEST_GCP_REPO_BUCKET
46+
from tests.utils.httpd import ContentMD5Handler
4647
from tests.utils.httpd import StaticFileServer
4748

4849

@@ -1176,7 +1177,7 @@ def test(self):
11761177

11771178
self.dvc.remove("imported_file.dvc")
11781179

1179-
with StaticFileServer(handler="Content-MD5") as httpd:
1180+
with StaticFileServer(handler_class=ContentMD5Handler) as httpd:
11801181
import_url = urljoin(self.get_remote(httpd.server_port), self.FOO)
11811182
import_output = "imported_file"
11821183
import_stage = self.dvc.imp_url(import_url, import_output)

tests/unit/remote/test_http.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import pytest
2+
from BaseHTTPServer import BaseHTTPRequestHandler
23

34
from dvc.config import ConfigError
5+
from dvc.exceptions import HTTPErrorStatusCodeException
6+
from dvc.path_info import URLInfo
47
from dvc.remote.http import RemoteHTTP
8+
from tests.utils.httpd import StaticFileServer
59

610

711
def test_no_traverse_compatibility(dvc_repo):
@@ -13,3 +17,23 @@ def test_no_traverse_compatibility(dvc_repo):
1317

1418
with pytest.raises(ConfigError):
1519
RemoteHTTP(dvc_repo, config)
20+
21+
22+
@pytest.mark.parametrize("response_code", [404, 403, 500])
23+
def test_download_fails_on_error_code(response_code, dvc_repo):
24+
class ErrorStatusRequestHandler(BaseHTTPRequestHandler):
25+
def do_GET(self):
26+
self.send_response(response_code, message="Error message")
27+
self.end_headers()
28+
29+
with StaticFileServer(ErrorStatusRequestHandler) as httpd:
30+
url = "http://localhost:{}/".format(httpd.server_port)
31+
config = {"url": url}
32+
33+
remote = RemoteHTTP(dvc_repo, config)
34+
import os
35+
36+
with pytest.raises(HTTPErrorStatusCodeException):
37+
remote._download(
38+
URLInfo(os.path.join(url, "file.txt")), "file.txt"
39+
)

tests/utils/httpd.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ class ContentMD5Handler(TestRequestHandler):
4545
class StaticFileServer:
4646
_lock = threading.Lock()
4747

48-
def __init__(self, handler="etag"):
48+
def __init__(self, handler_class=ETagHandler):
4949
self._lock.acquire()
50-
handler_class = ETagHandler if handler == "etag" else ContentMD5Handler
50+
self.response_handler = handler_class
5151
self._httpd = HTTPServer(("localhost", 0), handler_class)
5252
self._thread = None
5353

0 commit comments

Comments
 (0)