summaryrefslogtreecommitdiff
path: root/src/leap/soledad/server/_blobs/resource.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/soledad/server/_blobs/resource.py')
-rw-r--r--src/leap/soledad/server/_blobs/resource.py54
1 files changed, 49 insertions, 5 deletions
diff --git a/src/leap/soledad/server/_blobs/resource.py b/src/leap/soledad/server/_blobs/resource.py
index a6c209f2..dd9af861 100644
--- a/src/leap/soledad/server/_blobs/resource.py
+++ b/src/leap/soledad/server/_blobs/resource.py
@@ -19,6 +19,8 @@ A Twisted Web resource for blobs.
"""
import json
+from twisted.python.compat import intToBytes
+from twisted.python.compat import networkString
from twisted.web import resource
from twisted.web.client import FileBodyProducer
from twisted.web.server import NOT_DONE_YET
@@ -31,6 +33,7 @@ from .errors import BlobNotFound
from .errors import BlobExists
from .errors import ImproperlyConfiguredException
from .errors import QuotaExceeded
+from .errors import RangeNotSatisfiable
from .util import VALID_STRINGS
from leap.soledad.common.log import getLogger
@@ -44,7 +47,7 @@ def _catchBlobNotFound(failure, request, user, blob_id):
logger.error("Error 404: Blob %s does not exist for user %s"
% (blob_id, user))
request.setResponseCode(404)
- request.write("Blob doesn't exists: %s" % blob_id)
+ request.write("Blob doesn't exist: %s" % blob_id)
request.finish()
@@ -128,7 +131,7 @@ class BlobsResource(resource.Resource):
d.addErrback(_catchAllErrors, request)
return NOT_DONE_YET
- def _get_blob(self, request, user, blob_id, namespace):
+ def _get_blob(self, request, user, blob_id, namespace, range):
def _set_tag_header(tag):
request.responseHeaders.setRawHeaders('Tag', [tag])
@@ -136,18 +139,32 @@ class BlobsResource(resource.Resource):
def _read_blob(_):
handler = self._handler
consumer = request
- d = handler.read_blob(user, blob_id, consumer, namespace=namespace)
+ d = handler.read_blob(
+ user, blob_id, consumer, namespace=namespace, range=range)
return d
d = self._handler.get_tag(user, blob_id, namespace)
d.addCallback(_set_tag_header)
d.addCallback(_read_blob)
- d.addCallback(lambda _: request.finish())
d.addErrback(_catchBlobNotFound, request, user, blob_id)
d.addErrback(_catchAllErrors, request, finishRequest=True)
return NOT_DONE_YET
+ def _parseRange(self, range):
+ if not range:
+ return None
+ try:
+ kind, value = range.split(b'=', 1)
+ if kind.strip() != b'bytes':
+ raise Exception('Unknown unit: %s' % kind)
+ start, end = value.split('-')
+ start = int(start) if start else None
+ end = int(end) if end else None
+ return start, end
+ except Exception as e:
+ raise RangeNotSatisfiable(e)
+
def render_GET(self, request):
logger.info("http get: %s" % request.path)
user, blob_id, namespace = self._validate(request)
@@ -162,7 +179,34 @@ class BlobsResource(resource.Resource):
if only_flags:
return self._only_flags(request, user, blob_id, namespace)
- return self._get_blob(request, user, blob_id, namespace)
+ def _handleRangeHeader(size):
+ try:
+ range = self._parseRange(request.getHeader('Range'))
+ except RangeNotSatisfiable:
+ content_range = 'bytes */%d' % size
+ content_range = networkString(content_range)
+ request.setResponseCode(416)
+ request.setHeader(b'content-range', content_range)
+ request.finish()
+ return
+
+ if not range:
+ start = end = None
+ request.setResponseCode(200)
+ request.setHeader(b'content-length', intToBytes(size))
+ else:
+ start, end = range
+ content_range = 'bytes %d-%d/%d' % (start, end, size)
+ content_range = networkString(content_range)
+ length = intToBytes(end - start)
+ request.setResponseCode(206)
+ request.setHeader(b'content-range', content_range)
+ request.setHeader(b'content-length', length)
+ return self._get_blob(request, user, blob_id, namespace, range)
+
+ d = self._handler.get_blob_size(user, blob_id, namespace=namespace)
+ d.addCallback(_handleRangeHeader)
+ return NOT_DONE_YET
def render_DELETE(self, request):
logger.info("http put: %s" % request.path)