From 90a25d010953b1ddf702d27d21d354c13a703d2c Mon Sep 17 00:00:00 2001 From: Itamar Turner-Trauring Date: Thu, 6 Jan 2022 12:36:46 -0500 Subject: [PATCH] Reorganize into shared file. --- src/allmydata/storage/http_client.py | 124 ++++++++++++++++----------- src/allmydata/storage/http_common.py | 25 ++++++ src/allmydata/storage/http_server.py | 12 +-- 3 files changed, 99 insertions(+), 62 deletions(-) create mode 100644 src/allmydata/storage/http_common.py diff --git a/src/allmydata/storage/http_client.py b/src/allmydata/storage/http_client.py index a13ab1ce6..cdcb94a94 100644 --- a/src/allmydata/storage/http_client.py +++ b/src/allmydata/storage/http_client.py @@ -24,7 +24,7 @@ from base64 import b64encode import attr # TODO Make sure to import Python version? -from cbor2 import loads +from cbor2 import loads, dumps from twisted.web.http_headers import Headers @@ -32,6 +32,8 @@ from twisted.internet.defer import inlineCallbacks, returnValue, fail, Deferred from hyperlink import DecodedURL import treq +from .http_common import swissnum_auth_header, Secrets + class ClientException(Exception): """An unexpected error.""" @@ -44,11 +46,6 @@ def _decode_cbor(response): return fail(ClientException(response.code, response.phrase)) -def swissnum_auth_header(swissnum): # type: (bytes) -> bytes - """Return value for ``Authentication`` header.""" - return b"Tahoe-LAFS " + b64encode(swissnum).strip() - - @attr.s class ImmutableCreateResult(object): """Result of creating a storage index for an immutable.""" @@ -57,12 +54,75 @@ class ImmutableCreateResult(object): allocated = attr.ib(type=Set[int]) +class StorageClient(object): + """ + HTTP client that talks to the HTTP storage server. + """ + + def __init__( + self, url, swissnum, treq=treq + ): # type: (DecodedURL, bytes, Union[treq,StubTreq]) -> None + self._base_url = url + self._swissnum = swissnum + self._treq = treq + + def _url(self, path): + """Get a URL relative to the base URL.""" + return self._base_url.click(path) + + def _get_headers(self): # type: () -> Headers + """Return the basic headers to be used by default.""" + headers = Headers() + headers.addRawHeader( + "Authorization", + swissnum_auth_header(self._swissnum), + ) + return headers + + def _request( + self, + method, + url, + lease_renewal_secret=None, + lease_cancel_secret=None, + upload_secret=None, + **kwargs + ): + """ + Like ``treq.request()``, but with optional secrets that get translated + into corresponding HTTP headers. + """ + headers = self._get_headers() + for secret, value in [ + (Secrets.LEASE_RENEW, lease_renewal_secret), + (Secrets.LEASE_CANCEL, lease_cancel_secret), + (Secrets.UPLOAD, upload_secret), + ]: + if value is None: + continue + headers.addRawHeader( + "X-Tahoe-Authorization", + b"%s %s" % (secret.value.encode("ascii"), b64encode(value).strip()), + ) + return self._treq.request(method, url, headers=headers, **kwargs) + + @inlineCallbacks + def get_version(self): + """ + Return the version metadata for the server. + """ + url = self._url("/v1/version") + response = yield self._request("GET", url, {}) + decoded_response = yield _decode_cbor(response) + returnValue(decoded_response) + + class StorageClientImmutables(object): """ APIs for interacting with immutables. """ - def __init__(self, client): # type: (StorageClient) -> None + def __init__(self, client: StorageClient):# # type: (StorageClient) -> None self._client = client @inlineCallbacks @@ -87,6 +147,11 @@ class StorageClientImmutables(object): Result fires when creating the storage index succeeded, if creating the storage index failed the result will fire with an exception. """ + url = self._client._url("/v1/immutable/" + str(storage_index, "ascii")) + message = dumps( + {"share-numbers": share_numbers, "allocated-size": allocated_size} + ) + self._client._request("POST", ) @inlineCallbacks def write_share_chunk( @@ -121,48 +186,3 @@ class StorageClientImmutables(object): the HTTP protocol will be simplified, see https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3777 """ - - -class StorageClient(object): - """ - HTTP client that talks to the HTTP storage server. - """ - - def __init__( - self, url, swissnum, treq=treq - ): # type: (DecodedURL, bytes, Union[treq,StubTreq]) -> None - self._base_url = url - self._swissnum = swissnum - self._treq = treq - - def _get_headers(self): # type: () -> Headers - """Return the basic headers to be used by default.""" - headers = Headers() - headers.addRawHeader( - "Authorization", - swissnum_auth_header(self._swissnum), - ) - return headers - - def _request(self, method, url, secrets, **kwargs): - """ - Like ``treq.request()``, but additional argument of secrets mapping - ``http_server.Secret`` to the bytes value of the secret. - """ - headers = self._get_headers() - for key, value in secrets.items(): - headers.addRawHeader( - "X-Tahoe-Authorization", - b"%s %s" % (key.value.encode("ascii"), b64encode(value).strip()), - ) - return self._treq.request(method, url, headers=headers, **kwargs) - - @inlineCallbacks - def get_version(self): - """ - Return the version metadata for the server. - """ - url = self._base_url.click("/v1/version") - response = yield self._request("GET", url, {}) - decoded_response = yield _decode_cbor(response) - returnValue(decoded_response) diff --git a/src/allmydata/storage/http_common.py b/src/allmydata/storage/http_common.py new file mode 100644 index 000000000..af4224bd0 --- /dev/null +++ b/src/allmydata/storage/http_common.py @@ -0,0 +1,25 @@ +""" +Common HTTP infrastructure for the storge server. +""" +from future.utils import PY2 + +if PY2: + # fmt: off + from future.builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401 + # fmt: on + +from enum import Enum +from base64 import b64encode + + +def swissnum_auth_header(swissnum): # type: (bytes) -> bytes + """Return value for ``Authentication`` header.""" + return b"Tahoe-LAFS " + b64encode(swissnum).strip() + + +class Secrets(Enum): + """Different kinds of secrets the client may send.""" + + LEASE_RENEW = "lease-renew-secret" + LEASE_CANCEL = "lease-cancel-secret" + UPLOAD = "upload-secret" diff --git a/src/allmydata/storage/http_server.py b/src/allmydata/storage/http_server.py index 2dfb49b65..78752e9c5 100644 --- a/src/allmydata/storage/http_server.py +++ b/src/allmydata/storage/http_server.py @@ -17,7 +17,6 @@ else: from typing import Dict, List, Set from functools import wraps -from enum import Enum from base64 import b64decode from klein import Klein @@ -28,19 +27,11 @@ import attr from cbor2 import dumps, loads from .server import StorageServer -from .http_client import swissnum_auth_header +from .http_common import swissnum_auth_header, Secrets from .immutable import BucketWriter from ..util.hashutil import timing_safe_compare -class Secrets(Enum): - """Different kinds of secrets the client may send.""" - - LEASE_RENEW = "lease-renew-secret" - LEASE_CANCEL = "lease-cancel-secret" - UPLOAD = "upload-secret" - - class ClientSecretsException(Exception): """The client did not send the appropriate secrets.""" @@ -201,3 +192,4 @@ class HTTPServer(object): # New upload. # TODO self._storage_server.allocate_buckets() with given inputs. # TODO add results to self._uploads. + pass