Reorganize into shared file.

This commit is contained in:
Itamar Turner-Trauring 2022-01-06 12:36:46 -05:00
parent 9c20ac8e7b
commit 90a25d0109
3 changed files with 99 additions and 62 deletions

View File

@ -24,7 +24,7 @@ from base64 import b64encode
import attr import attr
# TODO Make sure to import Python version? # TODO Make sure to import Python version?
from cbor2 import loads from cbor2 import loads, dumps
from twisted.web.http_headers import Headers from twisted.web.http_headers import Headers
@ -32,6 +32,8 @@ from twisted.internet.defer import inlineCallbacks, returnValue, fail, Deferred
from hyperlink import DecodedURL from hyperlink import DecodedURL
import treq import treq
from .http_common import swissnum_auth_header, Secrets
class ClientException(Exception): class ClientException(Exception):
"""An unexpected error.""" """An unexpected error."""
@ -44,11 +46,6 @@ def _decode_cbor(response):
return fail(ClientException(response.code, response.phrase)) 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 @attr.s
class ImmutableCreateResult(object): class ImmutableCreateResult(object):
"""Result of creating a storage index for an immutable.""" """Result of creating a storage index for an immutable."""
@ -57,12 +54,75 @@ class ImmutableCreateResult(object):
allocated = attr.ib(type=Set[int]) 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): class StorageClientImmutables(object):
""" """
APIs for interacting with immutables. APIs for interacting with immutables.
""" """
def __init__(self, client): # type: (StorageClient) -> None def __init__(self, client: StorageClient):# # type: (StorageClient) -> None
self._client = client self._client = client
@inlineCallbacks @inlineCallbacks
@ -87,6 +147,11 @@ class StorageClientImmutables(object):
Result fires when creating the storage index succeeded, if creating the Result fires when creating the storage index succeeded, if creating the
storage index failed the result will fire with an exception. 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 @inlineCallbacks
def write_share_chunk( def write_share_chunk(
@ -121,48 +186,3 @@ class StorageClientImmutables(object):
the HTTP protocol will be simplified, see the HTTP protocol will be simplified, see
https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3777 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)

View File

@ -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"

View File

@ -17,7 +17,6 @@ else:
from typing import Dict, List, Set from typing import Dict, List, Set
from functools import wraps from functools import wraps
from enum import Enum
from base64 import b64decode from base64 import b64decode
from klein import Klein from klein import Klein
@ -28,19 +27,11 @@ import attr
from cbor2 import dumps, loads from cbor2 import dumps, loads
from .server import StorageServer from .server import StorageServer
from .http_client import swissnum_auth_header from .http_common import swissnum_auth_header, Secrets
from .immutable import BucketWriter from .immutable import BucketWriter
from ..util.hashutil import timing_safe_compare 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): class ClientSecretsException(Exception):
"""The client did not send the appropriate secrets.""" """The client did not send the appropriate secrets."""
@ -201,3 +192,4 @@ class HTTPServer(object):
# New upload. # New upload.
# TODO self._storage_server.allocate_buckets() with given inputs. # TODO self._storage_server.allocate_buckets() with given inputs.
# TODO add results to self._uploads. # TODO add results to self._uploads.
pass