mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-05-02 17:12:59 +00:00
Use pre-existing parser for Range and Content-Range headers.
This commit is contained in:
parent
d5bac8e186
commit
f09aa8c796
@ -640,7 +640,7 @@ For example::
|
|||||||
|
|
||||||
Read a contiguous sequence of bytes from one share in one bucket.
|
Read a contiguous sequence of bytes from one share in one bucket.
|
||||||
The response body is the raw share data (i.e., ``application/octet-stream``).
|
The response body is the raw share data (i.e., ``application/octet-stream``).
|
||||||
The ``Range`` header may be used to request exactly one ``bytes`` range.
|
The ``Range`` header may be used to request exactly one ``bytes`` range, in which case the response code will be 206 (partial content).
|
||||||
Interpretation and response behavior is as specified in RFC 7233 § 4.1.
|
Interpretation and response behavior is as specified in RFC 7233 § 4.1.
|
||||||
Multiple ranges in a single request are *not* supported.
|
Multiple ranges in a single request are *not* supported.
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
, setuptools, setuptoolsTrial, pyasn1, zope_interface
|
, setuptools, setuptoolsTrial, pyasn1, zope_interface
|
||||||
, service-identity, pyyaml, magic-wormhole, treq, appdirs
|
, service-identity, pyyaml, magic-wormhole, treq, appdirs
|
||||||
, beautifulsoup4, eliot, autobahn, cryptography, netifaces
|
, beautifulsoup4, eliot, autobahn, cryptography, netifaces
|
||||||
, html5lib, pyutil, distro, configparser, klein, cbor2
|
, html5lib, pyutil, distro, configparser, klein, werkzeug, cbor2
|
||||||
}:
|
}:
|
||||||
python.pkgs.buildPythonPackage rec {
|
python.pkgs.buildPythonPackage rec {
|
||||||
# Most of the time this is not exactly the release version (eg 1.17.0).
|
# Most of the time this is not exactly the release version (eg 1.17.0).
|
||||||
@ -98,7 +98,7 @@ EOF
|
|||||||
service-identity pyyaml magic-wormhole
|
service-identity pyyaml magic-wormhole
|
||||||
eliot autobahn cryptography netifaces setuptools
|
eliot autobahn cryptography netifaces setuptools
|
||||||
future pyutil distro configparser collections-extended
|
future pyutil distro configparser collections-extended
|
||||||
klein cbor2 treq
|
klein werkzeug cbor2 treq
|
||||||
];
|
];
|
||||||
|
|
||||||
checkInputs = with python.pkgs; [
|
checkInputs = with python.pkgs; [
|
||||||
|
1
setup.py
1
setup.py
@ -143,6 +143,7 @@ install_requires = [
|
|||||||
|
|
||||||
# HTTP server and client
|
# HTTP server and client
|
||||||
"klein",
|
"klein",
|
||||||
|
"werkzeug",
|
||||||
"treq",
|
"treq",
|
||||||
"cbor2"
|
"cbor2"
|
||||||
]
|
]
|
||||||
|
@ -261,7 +261,7 @@ class StorageClientImmutables(object):
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if response.code == 200:
|
if response.code == http.PARTIAL_CONTENT:
|
||||||
body = yield response.content()
|
body = yield response.content()
|
||||||
returnValue(body)
|
returnValue(body)
|
||||||
else:
|
else:
|
||||||
|
@ -22,6 +22,7 @@ from base64 import b64decode
|
|||||||
from klein import Klein
|
from klein import Klein
|
||||||
from twisted.web import http
|
from twisted.web import http
|
||||||
import attr
|
import attr
|
||||||
|
from werkzeug.http import parse_range_header, parse_content_range_header
|
||||||
|
|
||||||
# TODO Make sure to use pure Python versions?
|
# TODO Make sure to use pure Python versions?
|
||||||
from cbor2 import dumps, loads
|
from cbor2 import dumps, loads
|
||||||
@ -218,11 +219,12 @@ class HTTPServer(object):
|
|||||||
def write_share_data(self, request, authorization, storage_index, share_number):
|
def write_share_data(self, request, authorization, storage_index, share_number):
|
||||||
"""Write data to an in-progress immutable upload."""
|
"""Write data to an in-progress immutable upload."""
|
||||||
storage_index = si_a2b(storage_index.encode("ascii"))
|
storage_index = si_a2b(storage_index.encode("ascii"))
|
||||||
content_range = request.getHeader("content-range")
|
content_range = parse_content_range_header(request.getHeader("content-range"))
|
||||||
if content_range is None:
|
# TODO in https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3860
|
||||||
offset = 0
|
# 1. Malformed header should result in error
|
||||||
else:
|
# 2. Non-bytes unit should result in error
|
||||||
offset = int(content_range.split()[1].split("-")[0])
|
# 3. Missing header means full upload in one request
|
||||||
|
offset = content_range.start
|
||||||
|
|
||||||
# TODO basic checks on validity of start, offset, and content-range in general. also of share_number.
|
# TODO basic checks on validity of start, offset, and content-range in general. also of share_number.
|
||||||
# TODO basic check that body isn't infinite. require content-length? or maybe we should require content-range (it's optional now)? if so, needs to be rflected in protocol spec.
|
# TODO basic check that body isn't infinite. require content-length? or maybe we should require content-range (it's optional now)? if so, needs to be rflected in protocol spec.
|
||||||
@ -256,22 +258,21 @@ class HTTPServer(object):
|
|||||||
)
|
)
|
||||||
def read_share_chunk(self, request, authorization, storage_index, share_number):
|
def read_share_chunk(self, request, authorization, storage_index, share_number):
|
||||||
"""Read a chunk for an already uploaded immutable."""
|
"""Read a chunk for an already uploaded immutable."""
|
||||||
# TODO basic checks on validity
|
# TODO in https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3860
|
||||||
|
# 1. basic checks on validity on storage index, share number
|
||||||
|
# 2. missing range header should have response code 200 and return whole thing
|
||||||
|
# 3. malformed range header should result in error? or return everything?
|
||||||
|
# 4. non-bytes range results in error
|
||||||
|
# 5. ranges make sense semantically (positive, etc.)
|
||||||
|
# 6. multiple ranges fails with error
|
||||||
|
# 7. missing end of range means "to the end of share"
|
||||||
storage_index = si_a2b(storage_index.encode("ascii"))
|
storage_index = si_a2b(storage_index.encode("ascii"))
|
||||||
range_header = request.getHeader("range")
|
range_header = parse_range_header(request.getHeader("range"))
|
||||||
if range_header is None:
|
offset, end = range_header.ranges[0]
|
||||||
offset = 0
|
assert end != None # TODO support this case
|
||||||
inclusive_end = None
|
|
||||||
else:
|
|
||||||
parts = range_header.split("=")[1].split("-")
|
|
||||||
offset = int(parts[0]) # TODO make sure valid
|
|
||||||
if len(parts) > 0:
|
|
||||||
inclusive_end = int(parts[1]) # TODO make sure valid
|
|
||||||
else:
|
|
||||||
inclusive_end = None
|
|
||||||
|
|
||||||
assert inclusive_end != None # TODO support this case
|
|
||||||
|
|
||||||
# TODO if not found, 404
|
# TODO if not found, 404
|
||||||
bucket = self._storage_server.get_buckets(storage_index)[share_number]
|
bucket = self._storage_server.get_buckets(storage_index)[share_number]
|
||||||
return bucket.read(offset, inclusive_end - offset + 1)
|
data = bucket.read(offset, end - offset)
|
||||||
|
request.setResponseCode(http.PARTIAL_CONTENT)
|
||||||
|
return data
|
||||||
|
@ -339,7 +339,7 @@ class ImmutableHTTPAPITests(AsyncTestCase):
|
|||||||
self.assertTrue(finished)
|
self.assertTrue(finished)
|
||||||
|
|
||||||
# We can now read:
|
# We can now read:
|
||||||
for offset, length in [(0, 100), (10, 19), (99, 0), (49, 200)]:
|
for offset, length in [(0, 100), (10, 19), (99, 1), (49, 200)]:
|
||||||
downloaded = yield im_client.read_share_chunk(
|
downloaded = yield im_client.read_share_chunk(
|
||||||
storage_index, 1, offset, length
|
storage_index, 1, offset, length
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user