From 305fe2e8179f6eb1001db59788541c417aa9f97d Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 28 Apr 2018 17:42:02 +0700 Subject: [PATCH] Handle asyncio timeouts. Ref #1307. --- gns3server/compute/docker/__init__.py | 6 +++++- gns3server/controller/export_project.py | 10 ++++++++-- .../handlers/api/compute/project_handler.py | 15 +++++++++------ .../handlers/api/controller/node_handler.py | 4 +--- .../handlers/api/controller/project_handler.py | 15 +++++++++------ .../handlers/api/controller/symbol_handler.py | 11 ++++++++--- 6 files changed, 40 insertions(+), 21 deletions(-) diff --git a/gns3server/compute/docker/__init__.py b/gns3server/compute/docker/__init__.py index d2fca166..e746f30e 100644 --- a/gns3server/compute/docker/__init__.py +++ b/gns3server/compute/docker/__init__.py @@ -198,7 +198,7 @@ class Docker(BaseManager): pass if progress_callback: - progress_callback("Pull {} from docker hub".format(image)) + progress_callback("Pulling '{}' from docker hub".format(image)) response = yield from self.http_query("POST", "images/create", params={"fromImage": image}, timeout=None) # The pull api will stream status via an HTTP JSON stream content = "" @@ -206,6 +206,10 @@ class Docker(BaseManager): try: chunk = yield from response.content.read(1024) except aiohttp.ServerDisconnectedError: + log.error("Disconnected from server while pulling Docker image '{}' from docker hub".format(image)) + break + except asyncio.TimeoutError: + log.error("Timeout while pulling Docker image '{}' from docker hub".format(image)) break if not chunk: break diff --git a/gns3server/controller/export_project.py b/gns3server/controller/export_project.py index 37faaf38..df727574 100644 --- a/gns3server/controller/export_project.py +++ b/gns3server/controller/export_project.py @@ -90,7 +90,10 @@ def export_project(project, temporary_dir, include_images=False, keep_compute_id f = open(fd, "wb", closefd=True) response = yield from compute.download_file(project, compute_file["path"]) while True: - data = yield from response.content.read(1024) + try: + data = yield from response.content.read(1024) + except asyncio.TimeoutError: + raise aiohttp.web.HTTPRequestTimeout(text="Timeout when downloading file '{}' from remote compute server {}:{}".format(compute_file["path"], compute.host, compute.port)) if not data: break f.write(data) @@ -250,7 +253,10 @@ def _export_remote_images(project, compute_id, image_type, image, project_zipfil raise aiohttp.web.HTTPConflict(text="Cannot export image from '{}' compute. Compute returned status code {}.".format(compute_id, response.status)) while True: - data = yield from response.content.read(1024) + try: + data = yield from response.content.read(1024) + except asyncio.TimeoutError: + raise aiohttp.web.HTTPRequestTimeout(text="Timeout when downloading image '{}' from remote compute server {}:{}".format(image, compute.host, compute.port)) if not data: break f.write(data) diff --git a/gns3server/handlers/api/compute/project_handler.py b/gns3server/handlers/api/compute/project_handler.py index 6b5968d3..904946cf 100644 --- a/gns3server/handlers/api/compute/project_handler.py +++ b/gns3server/handlers/api/compute/project_handler.py @@ -319,10 +319,13 @@ class ProjectHandler: os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, 'wb+') as f: while True: - packet = yield from request.content.read(1024) - if not packet: + try: + chunk = yield from request.content.read(1024) + except asyncio.TimeoutError: + raise aiohttp.web.HTTPRequestTimeout(text="Timeout when writing to file '{}'".format(path)) + if not chunk: break - f.write(packet) + f.write(chunk) except FileNotFoundError: raise aiohttp.web.HTTPNotFound() @@ -380,10 +383,10 @@ class ProjectHandler: try: with tempfile.SpooledTemporaryFile(max_size=10000) as temp: while True: - packet = yield from request.content.read(1024) - if not packet: + chunk = yield from request.content.read(1024) + if not chunk: break - temp.write(packet) + temp.write(chunk) project.import_zip(temp, gns3vm=bool(int(request.GET.get("gns3vm", "1")))) except OSError as e: raise aiohttp.web.HTTPInternalServerError(text="Could not import the project: {}".format(e)) diff --git a/gns3server/handlers/api/controller/node_handler.py b/gns3server/handlers/api/controller/node_handler.py index e02b45f2..65e3a308 100644 --- a/gns3server/handlers/api/controller/node_handler.py +++ b/gns3server/handlers/api/controller/node_handler.py @@ -409,8 +409,6 @@ class NodeHandler: node_type = node.node_type path = "/project-files/{}/{}/{}".format(node_type, node.id, path) - - data = yield from request.content.read() - + data = yield from request.content.read() #FIXME: are we handling timeout or large files correctly? yield from node.compute.http_query("POST", "/projects/{project_id}/files{path}".format(project_id=project.id, path=path), data=data, timeout=None, raw=True) response.set_status(201) diff --git a/gns3server/handlers/api/controller/project_handler.py b/gns3server/handlers/api/controller/project_handler.py index 501fec5a..e487c1d7 100644 --- a/gns3server/handlers/api/controller/project_handler.py +++ b/gns3server/handlers/api/controller/project_handler.py @@ -335,10 +335,10 @@ class ProjectHandler: try: with tempfile.SpooledTemporaryFile(max_size=10000) as temp: while True: - packet = yield from request.content.read(1024) - if not packet: + chunk = yield from request.content.read(1024) + if not chunk: break - temp.write(packet) + temp.write(chunk) project = yield from import_project(controller, request.match_info["project_id"], temp, location=path, name=name) except OSError as e: raise aiohttp.web.HTTPInternalServerError(text="Could not import the project: {}".format(e)) @@ -448,10 +448,13 @@ class ProjectHandler: try: with open(path, 'wb+') as f: while True: - packet = yield from request.content.read(1024) - if not packet: + try: + chunk = yield from request.content.read(1024) + except asyncio.TimeoutError: + raise aiohttp.web.HTTPRequestTimeout(text="Timeout when writing to file '{}'".format(path)) + if not chunk: break - f.write(packet) + f.write(chunk) except FileNotFoundError: raise aiohttp.web.HTTPNotFound() except PermissionError: diff --git a/gns3server/handlers/api/controller/symbol_handler.py b/gns3server/handlers/api/controller/symbol_handler.py index 7cac242c..b9796d94 100644 --- a/gns3server/handlers/api/controller/symbol_handler.py +++ b/gns3server/handlers/api/controller/symbol_handler.py @@ -17,6 +17,8 @@ import os import aiohttp +import asyncio + from gns3server.web.route import Route from gns3server.controller import Controller @@ -66,10 +68,13 @@ class SymbolHandler: try: with open(path, 'wb') as f: while True: - packet = yield from request.content.read(1024) - if not packet: + try: + chunk = yield from request.content.read(1024) + except asyncio.TimeoutError: + raise aiohttp.web.HTTPRequestTimeout(text="Timeout when writing to symbol '{}'".format(path)) + if not chunk: break - f.write(packet) + f.write(chunk) except OSError as e: raise aiohttp.web.HTTPConflict(text="Could not write symbol file '{}': {}".format(path, e)) # Reset the symbol list