From e8cf7f5bfbf5cfe83c82e06ab1ad028642bdf44a Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Mon, 27 Nov 2017 08:47:12 -0300 Subject: [feature] add a streaming resource -- Related: #8809 --- src/leap/soledad/server/_streaming_resource.py | 69 ++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/leap/soledad/server/_streaming_resource.py (limited to 'src/leap/soledad/server/_streaming_resource.py') diff --git a/src/leap/soledad/server/_streaming_resource.py b/src/leap/soledad/server/_streaming_resource.py new file mode 100644 index 00000000..9805bb7e --- /dev/null +++ b/src/leap/soledad/server/_streaming_resource.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +# _streaming_resource.py +# Copyright (C) 2017 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +A twisted resource that serves download as a single stream of multiple blobs. +-> POST .../uuid/namespace/ DATA: [blob_id, blob_id2, ..., blob_idn] +<- [(size(blob_id), content(blob_id)) for blob_id in DATA] (as a binary stream) +""" +import json +import struct + +from twisted.web.server import NOT_DONE_YET +from twisted.web.resource import Resource + +from leap.soledad.common.log import getLogger +from . import interfaces +from ._blobs import FilesystemBlobsBackend +from ._blobs import ImproperlyConfiguredException + + +__all__ = ['StreamingResource'] + + +logger = getLogger(__name__) +SIZE_PACKER = struct.Struct("I") + + +class StreamingResource(Resource): + isLeaf = True + + # Allowed backend classes are defined here + handlers = {"filesystem": FilesystemBlobsBackend} + + def __init__(self, backend, blobs_path, **backend_kwargs): + Resource.__init__(self) + self._blobs_path = blobs_path + backend_kwargs.update({'blobs_path': blobs_path}) + if backend not in self.handlers: + raise ImproperlyConfiguredException("No such backend: %s", backend) + self._handler = self.handlers[backend](**backend_kwargs) + assert interfaces.IBlobsBackend.providedBy(self._handler) + + def render_POST(self, request): + user, namespace = request.postpath + db = self.factory.open_database(user) + raw_content = request.content.read() + blob_ids = json.loads(raw_content) + for blob_id in blob_ids: + path = db._get_path(user, blob_id, namespace) + size = db.get_blob_size(user, blob_id, namespace) + request.write(SIZE_PACKER.pack(size)) + with open(path, 'rb') as blob_fd: + request.content.write(blob_fd.read()) + + request.finish() + return NOT_DONE_YET -- cgit v1.2.3