From 21c3c50e37114a7a3e7e6d02ebe08af1101c45f3 Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring Date: Tue, 19 Apr 2022 15:07:57 -0400 Subject: [PATCH] Basic mutable read support. --- src/allmydata/storage/http_server.py | 43 ++++++++++++++++++++++++++++ src/allmydata/storage_client.py | 13 ++++----- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/allmydata/storage/http_server.py b/src/allmydata/storage/http_server.py index 3eae476b7..dbb79cf2b 100644 --- a/src/allmydata/storage/http_server.py +++ b/src/allmydata/storage/http_server.py @@ -601,6 +601,49 @@ class HTTPServer(object): ) return self._send_encoded(request, {"success": success, "data": read_data}) + @_authorized_route( + _app, + set(), + "/v1/mutable//", + methods=["GET"], + ) + def read_mutable_chunk(self, request, authorization, storage_index, share_number): + """Read a chunk from a mutable.""" + if request.getHeader("range") is None: + # TODO in follow-up ticket + raise NotImplementedError() + + # TODO reduce duplication with immutable reads? + # TODO unit tests, perhaps shared if possible + range_header = parse_range_header(request.getHeader("range")) + if ( + range_header is None + or range_header.units != "bytes" + or len(range_header.ranges) > 1 # more than one range + or range_header.ranges[0][1] is None # range without end + ): + request.setResponseCode(http.REQUESTED_RANGE_NOT_SATISFIABLE) + return b"" + + offset, end = range_header.ranges[0] + + # TODO limit memory usage + # https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3872 + data = self._storage_server.slot_readv( + storage_index, [share_number], [(offset, end - offset)] + )[share_number][0] + + # TODO reduce duplication? + request.setResponseCode(http.PARTIAL_CONTENT) + if len(data): + # For empty bodies the content-range header makes no sense since + # the end of the range is inclusive. + request.setHeader( + "content-range", + ContentRange("bytes", offset, offset + len(data)).to_header(), + ) + return data + @implementer(IStreamServerEndpoint) @attr.s diff --git a/src/allmydata/storage_client.py b/src/allmydata/storage_client.py index 5321efb7d..857b19ed7 100644 --- a/src/allmydata/storage_client.py +++ b/src/allmydata/storage_client.py @@ -1195,18 +1195,17 @@ class _HTTPStorageServer(object): def slot_readv(self, storage_index, shares, readv): mutable_client = StorageClientMutables(self._http_client) reads = {} + # TODO if shares list is empty, that means list all shares, so we need + # to do a query to get that. + assert shares # TODO replace with call to list shares for share_number in shares: share_reads = reads[share_number] = [] for (offset, length) in readv: - d = mutable_client.read_share_chunk( + r = yield mutable_client.read_share_chunk( storage_index, share_number, offset, length ) - share_reads.append(d) - result = { - share_number: [(yield d) for d in share_reads] - for (share_number, reads) in reads.items() - } - defer.returnValue(result) + share_reads.append(r) + defer.returnValue(reads) @defer.inlineCallbacks def slot_testv_and_readv_and_writev(