From 94ddb9f80273a3db3fedfdc7a846a1ebeedd0003 Mon Sep 17 00:00:00 2001 From: drebs Date: Sat, 9 Dec 2017 08:09:34 -0200 Subject: [refactor] make read_blob() return a deferred --- src/leap/soledad/server/_blobs.py | 63 +++++++++++++++++++++++++---------- src/leap/soledad/server/interfaces.py | 7 ++-- 2 files changed, 50 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/leap/soledad/server/_blobs.py b/src/leap/soledad/server/_blobs.py index bfe95516..4a963e10 100644 --- a/src/leap/soledad/server/_blobs.py +++ b/src/leap/soledad/server/_blobs.py @@ -29,6 +29,8 @@ import base64 import json import re +from twisted.python.compat import intToBytes +from twisted.web import http from twisted.web import static from twisted.web import resource from twisted.web.client import FileBodyProducer @@ -95,8 +97,8 @@ class FilesystemBlobsBackend(object): logger.info('reading blob: %s - %s@%s' % (user, blob_id, namespace)) path = self._get_path(user, blob_id, namespace) logger.debug('blob path: %s' % path) - res = static.File(path, defaultType='application/octet-stream') - return res + fd = open(path) + return defer.succeed(fd) def get_flags(self, user, blob_id, namespace=''): path = self._get_path(user, blob_id, namespace) @@ -257,6 +259,23 @@ class ImproperlyConfiguredException(Exception): pass +class BlobFile(resource.Resource): + + def __init__(self, fd): + self.fd = fd + self.fd.seek(0, 2) + self.size = self.fd.tell() + self.fd.seek(0) + + def render_GET(self, request): + request.setHeader(b'content-length', intToBytes(self.size)) + request.setHeader(b'content-type', 'application/octet-stream') + request.setResponseCode(http.OK) + producer = static.NoRangeStaticProducer(request, self.fd) + producer.start() + return NOT_DONE_YET + + class BlobsResource(resource.Resource): isLeaf = True @@ -296,22 +315,32 @@ class BlobsResource(resource.Resource): d.addCallback(lambda blobs: request.write(blobs)) d.addCallback(lambda _: request.finish()) return NOT_DONE_YET - only_flags = request.args.get('only_flags', [False])[0] - try: - if only_flags: - d = self._handler.get_flags(user, blob_id, namespace) - d.addCallback(lambda flags: json.dumps(flags)) - d.addCallback(lambda flags: request.write(flags)) - d.addCallback(lambda _: request.finish()) - return NOT_DONE_YET - tag = self._handler.get_tag(user, blob_id, namespace) - request.responseHeaders.setRawHeaders('Tag', [tag]) - except BlobNotFound: - # 404 - Not Found + + def catchBlobNotFound(failure): + failure.trap(BlobNotFound) request.setResponseCode(404) - return "Blob doesn't exists: %s" % blob_id - res = self._handler.read_blob(user, blob_id, namespace=namespace) - return res.render_GET(request) + request.write("Blob doesn't exists: %s" % blob_id) + request.finish() + + only_flags = request.args.get('only_flags', [False])[0] + if only_flags: + d = self._handler.get_flags(user, blob_id, namespace) + d.addErrback(catchBlobNotFound) + d.addCallback(lambda flags: json.dumps(flags)) + d.addCallback(lambda flags: request.write(flags)) + d.addCallback(lambda _: request.finish()) + return NOT_DONE_YET + + d = self._handler.get_tag(user, blob_id, namespace) + d.addCallback( + lambda tag: request.responseHeaders.setRawHeaders( + 'Tag', [tag])) + d.addCallback(lambda _: self._handler.read_blob(user, blob_id, + namespace=namespace)) + d.addCallback(lambda fd: BlobFile(fd)) + d.addCallback(lambda res: res.render_GET(request)) + + return NOT_DONE_YET def render_DELETE(self, request): logger.info("http put: %s" % request.path) diff --git a/src/leap/soledad/server/interfaces.py b/src/leap/soledad/server/interfaces.py index 358a0d62..d7a4aa70 100644 --- a/src/leap/soledad/server/interfaces.py +++ b/src/leap/soledad/server/interfaces.py @@ -27,7 +27,7 @@ class IBlobsBackend(Interface): def read_blob(user, blob_id, namespace=''): """ - Read a blob from the backend storage return it as a twisted resource. + Read a blob from the backend storage. :param user: The id of the user who owns the blob. :type user: str @@ -36,8 +36,9 @@ class IBlobsBackend(Interface): :param namespace: An optional namespace for the blob. :type namespace: str - :return: The blob as a twisted resource. - :rtype: twisted.web.resource.Resource + :return: A deferred that fires with a file-like object that gives + access to the contents of the blob. + :rtype: twisted.internet.defer.Deferred """ def write_blob(user, blob_id, fd, namespace=''): -- cgit v1.2.3