summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrebs <drebs@leap.se>2017-12-09 08:09:34 -0200
committerdrebs <drebs@leap.se>2017-12-13 13:49:27 -0200
commit94ddb9f80273a3db3fedfdc7a846a1ebeedd0003 (patch)
tree00426c4ffca2de63bf90999eadfdd6fe3852019d
parent8a1ab21e09632928be79fdf7473073ae363f8231 (diff)
[refactor] make read_blob() return a deferred
-rw-r--r--src/leap/soledad/server/_blobs.py63
-rw-r--r--src/leap/soledad/server/interfaces.py7
-rw-r--r--tests/blobs/test_fs_backend.py16
3 files changed, 55 insertions, 31 deletions
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=''):
diff --git a/tests/blobs/test_fs_backend.py b/tests/blobs/test_fs_backend.py
index 816cba39..28be4835 100644
--- a/tests/blobs/test_fs_backend.py
+++ b/tests/blobs/test_fs_backend.py
@@ -63,21 +63,15 @@ class FilesystemBackendTestCase(unittest.TestCase):
self.assertEquals(10, size)
@pytest.mark.usefixtures("method_tmpdir")
- @mock.patch.object(_blobs.static, 'File')
+ @mock.patch('leap.soledad.server._blobs.open')
@mock.patch.object(_blobs.FilesystemBlobsBackend, '_get_path',
Mock(return_value='path'))
- def test_read_blob(self, file_mock):
- render_mock = Mock()
- file_mock.return_value = render_mock
+ @defer.inlineCallbacks
+ def test_read_blob(self, open):
backend = _blobs.FilesystemBlobsBackend(blobs_path=self.tempdir)
- request = DummyRequest([''])
- resource = backend.read_blob('user', 'blob_id')
- resource.render_GET(request)
-
+ yield backend.read_blob('user', 'blob_id')
+ open.assert_called_once_with('path')
backend._get_path.assert_called_once_with('user', 'blob_id', '')
- ctype = 'application/octet-stream'
- _blobs.static.File.assert_called_once_with('path', defaultType=ctype)
- render_mock.render_GET.assert_called_once_with(request)
@pytest.mark.usefixtures("method_tmpdir")
@mock.patch.object(os.path, 'isfile')