Support lack of Range header.

This commit is contained in:
Itamar Turner-Trauring 2022-02-10 13:31:09 -05:00
parent 7db1ddd875
commit 416af7328c
2 changed files with 47 additions and 15 deletions

View File

@ -291,8 +291,24 @@ class HTTPServer(object):
)
def read_share_chunk(self, request, authorization, storage_index, share_number):
"""Read a chunk for an already uploaded immutable."""
# 2. missing range header should have response code 200 and return whole thing
# 7. missing end of range means "to the end of share"
try:
bucket = self._storage_server.get_buckets(storage_index)[share_number]
except KeyError:
request.setResponseCode(http.NOT_FOUND)
return b""
if request.getHeader("range") is None:
# Return the whole thing.
start = 0
while True:
# TODO should probably yield to event loop occasionally...
data = bucket.read(start, start + 65536)
if not data:
request.finish()
return
request.write(data)
start += len(data)
range_header = parse_range_header(request.getHeader("range"))
if (
range_header is None
@ -305,12 +321,6 @@ class HTTPServer(object):
offset, end = range_header.ranges[0]
try:
bucket = self._storage_server.get_buckets(storage_index)[share_number]
except KeyError:
request.setResponseCode(http.NOT_FOUND)
return b""
# TODO limit memory usage
data = bucket.read(offset, end - offset)
request.setResponseCode(http.PARTIAL_CONTENT)

View File

@ -46,6 +46,7 @@ from ..storage.http_client import (
ImmutableCreateResult,
UploadProgress,
StorageClientGeneral,
_encode_si,
)
from ..storage.common import si_b2a
@ -654,21 +655,26 @@ class ImmutableHTTPAPITests(SyncTestCase):
)
self.assertEqual(e.exception.code, http.NOT_FOUND)
def upload(self, share_number):
def upload(self, share_number, data_length=26):
"""
Create a share, return (storage_index).
Create a share, return (storage_index, uploaded_data).
"""
(upload_secret, _, storage_index, _) = self.create_upload({share_number}, 26)
uploaded_data = (b"abcdefghijklmnopqrstuvwxyz" * ((data_length // 26) + 1))[
:data_length
]
(upload_secret, _, storage_index, _) = self.create_upload(
{share_number}, data_length
)
result_of(
self.im_client.write_share_chunk(
storage_index,
share_number,
upload_secret,
0,
b"abcdefghijklmnopqrstuvwxyz",
uploaded_data,
)
)
return storage_index
return storage_index, uploaded_data
def test_read_of_wrong_storage_index_fails(self):
"""
@ -689,7 +695,7 @@ class ImmutableHTTPAPITests(SyncTestCase):
"""
Reading from unknown storage index results in 404.
"""
storage_index = self.upload(1)
storage_index, _ = self.upload(1)
with self.assertRaises(ClientException) as e:
result_of(
self.im_client.read_share_chunk(
@ -706,7 +712,7 @@ class ImmutableHTTPAPITests(SyncTestCase):
Malformed or unsupported Range headers result in 416 (requested range
not satisfiable) error.
"""
storage_index = self.upload(1)
storage_index, _ = self.upload(1)
def check_bad_range(bad_range_value):
client = StorageClientImmutables(
@ -738,3 +744,19 @@ class ImmutableHTTPAPITests(SyncTestCase):
# semantically valid under HTTP.
check_bad_range("bytes=0-")
@given(data_length=st.integers(min_value=1, max_value=300000))
def test_read_with_no_range(self, data_length):
"""
A read with no range returns the whole immutable.
"""
storage_index, uploaded_data = self.upload(1, data_length)
response = result_of(
self.http.client.request(
"GET",
self.http.client.relative_url(
"/v1/immutable/{}/1".format(_encode_si(storage_index))
),
)
)
self.assertEqual(response.code, http.OK)
self.assertEqual(result_of(response.content()), uploaded_data)