Refactor CloudApiClient (and ToolpathUploader) to use HttpRequestManager

Has the benefit of a more unified Http request management + timeouts

CURA-7290
This commit is contained in:
Nino van Hooff 2020-05-15 11:28:17 +02:00
parent 15f813a4ff
commit f3c66c3189
3 changed files with 74 additions and 58 deletions

View file

@ -1,11 +1,11 @@
# Copyright (c) 2019 Ultimaker B.V.
# !/usr/bin/env python
# -*- coding: utf-8 -*-
from PyQt5.QtCore import QUrl
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager
from typing import Optional, Callable, Any, Tuple, cast
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
from typing import Callable, Any, Tuple
from UM.Logger import Logger
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from ..Models.Http.CloudPrintJobResponse import CloudPrintJobResponse
@ -23,16 +23,16 @@ class ToolPathUploader:
BYTES_PER_REQUEST = 256 * 1024
## Creates a mesh upload object.
# \param manager: The network access manager that will handle the HTTP requests.
# \param http: The HttpRequestManager that will handle the HTTP requests.
# \param print_job: The print job response that was returned by the cloud after registering the upload.
# \param data: The mesh bytes to be uploaded.
# \param on_finished: The method to be called when done.
# \param on_progress: The method to be called when the progress changes (receives a percentage 0-100).
# \param on_error: The method to be called when an error occurs.
def __init__(self, manager: QNetworkAccessManager, print_job: CloudPrintJobResponse, data: bytes,
def __init__(self, http: HttpRequestManager, print_job: CloudPrintJobResponse, data: bytes,
on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any]
) -> None:
self._manager = manager
self._http = http
self._print_job = print_job
self._data = data
@ -43,25 +43,12 @@ class ToolPathUploader:
self._sent_bytes = 0
self._retries = 0
self._finished = False
self._reply = None # type: Optional[QNetworkReply]
## Returns the print job for which this object was created.
@property
def printJob(self):
return self._print_job
## Creates a network request to the print job upload URL, adding the needed content range header.
def _createRequest(self) -> QNetworkRequest:
request = QNetworkRequest(QUrl(self._print_job.upload_url))
request.setHeader(QNetworkRequest.ContentTypeHeader, self._print_job.content_type)
first_byte, last_byte = self._chunkRange()
content_range = "bytes {}-{}/{}".format(first_byte, last_byte - 1, len(self._data))
request.setRawHeader(b"Content-Range", content_range.encode())
Logger.log("i", "Uploading %s to %s", content_range, self._print_job.upload_url)
return request
## Determines the bytes that should be uploaded next.
# \return: A tuple with the first and the last byte to upload.
def _chunkRange(self) -> Tuple[int, int]:
@ -88,13 +75,23 @@ class ToolPathUploader:
raise ValueError("The upload is already finished")
first_byte, last_byte = self._chunkRange()
request = self._createRequest()
content_range = "bytes {}-{}/{}".format(first_byte, last_byte - 1, len(self._data))
# now send the reply and subscribe to the results
self._reply = self._manager.put(request, self._data[first_byte:last_byte])
self._reply.finished.connect(self._finishedCallback)
self._reply.uploadProgress.connect(self._progressCallback)
self._reply.error.connect(self._errorCallback)
headers = {
"Content-Type": self._print_job.content_type,
"Content-Range": content_range
}
Logger.log("i", "Uploading %s to %s", content_range, self._print_job.upload_url)
self._http.put(
url = self._print_job.upload_url,
headers_dict = headers,
data = self._data[first_byte:last_byte],
callback = self._finishedCallback,
error_callback = self._errorCallback,
upload_progress_callback = self._progressCallback
)
## Handles an update to the upload progress
# \param bytes_sent: The amount of bytes sent in the current request.
@ -106,16 +103,14 @@ class ToolPathUploader:
self._on_progress(int(total_sent / len(self._data) * 100))
## Handles an error uploading.
def _errorCallback(self) -> None:
reply = cast(QNetworkReply, self._reply)
def _errorCallback(self, reply: QNetworkReply, error: QNetworkReply.NetworkError) -> None:
body = bytes(reply.readAll()).decode()
Logger.log("e", "Received error while uploading: %s", body)
self.stop()
self._on_error()
## Checks whether a chunk of data was uploaded successfully, starting the next chunk if needed.
def _finishedCallback(self) -> None:
reply = cast(QNetworkReply, self._reply)
def _finishedCallback(self, reply: QNetworkReply) -> None:
Logger.log("i", "Finished callback %s %s",
reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url().toString())
@ -133,7 +128,7 @@ class ToolPathUploader:
# Http codes that are not to be retried are assumed to be errors.
if status_code > 308:
self._errorCallback()
self._errorCallback(reply, None)
return
Logger.log("d", "status_code: %s, Headers: %s, body: %s", status_code,