mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-06-19 07:48:11 +00:00
Hook up NURL generation to the new Foolscap/HTTPS protocol switch.
This commit is contained in:
@ -37,6 +37,7 @@ import allmydata
|
|||||||
from allmydata.crypto import rsa, ed25519
|
from allmydata.crypto import rsa, ed25519
|
||||||
from allmydata.crypto.util import remove_prefix
|
from allmydata.crypto.util import remove_prefix
|
||||||
from allmydata.storage.server import StorageServer, FoolscapStorageServer
|
from allmydata.storage.server import StorageServer, FoolscapStorageServer
|
||||||
|
from allmydata.storage.http_server import build_nurl
|
||||||
from allmydata import storage_client
|
from allmydata import storage_client
|
||||||
from allmydata.immutable.upload import Uploader
|
from allmydata.immutable.upload import Uploader
|
||||||
from allmydata.immutable.offloaded import Helper
|
from allmydata.immutable.offloaded import Helper
|
||||||
@ -658,6 +659,12 @@ class _Client(node.Node, pollmixin.PollMixin):
|
|||||||
if webport:
|
if webport:
|
||||||
self.init_web(webport) # strports string
|
self.init_web(webport) # strports string
|
||||||
|
|
||||||
|
# TODO this may be the wrong location for now? but as temporary measure
|
||||||
|
# it allows us to get NURLs for testing in test_istorageserver.py Will
|
||||||
|
# eventually get fixed one way or another in
|
||||||
|
# https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3901
|
||||||
|
self.storage_nurls = []
|
||||||
|
|
||||||
def init_stats_provider(self):
|
def init_stats_provider(self):
|
||||||
self.stats_provider = StatsProvider(self)
|
self.stats_provider = StatsProvider(self)
|
||||||
self.stats_provider.setServiceParent(self)
|
self.stats_provider.setServiceParent(self)
|
||||||
@ -820,6 +827,15 @@ class _Client(node.Node, pollmixin.PollMixin):
|
|||||||
furl = self.tub.registerReference(FoolscapStorageServer(ss), furlFile=furl_file)
|
furl = self.tub.registerReference(FoolscapStorageServer(ss), furlFile=furl_file)
|
||||||
(_, _, swissnum) = furl.rpartition("/")
|
(_, _, swissnum) = furl.rpartition("/")
|
||||||
self.tub.negotiationClass.add_storage_server(ss, swissnum.encode("ascii"))
|
self.tub.negotiationClass.add_storage_server(ss, swissnum.encode("ascii"))
|
||||||
|
for location_hint in self.tub.locationHints:
|
||||||
|
if location_hint.startswith("tcp:"):
|
||||||
|
_, hostname, port = location_hint.split(":")
|
||||||
|
port = int(port)
|
||||||
|
self.storage_nurls.append(
|
||||||
|
build_nurl(
|
||||||
|
hostname, port, swissnum, self.tub.myCertificate.original.to_cryptography()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
announcement["anonymous-storage-FURL"] = furl
|
announcement["anonymous-storage-FURL"] = furl
|
||||||
|
|
||||||
|
@ -66,14 +66,6 @@ class _FoolscapOrHttps(Protocol, metaclass=_PretendToBeNegotiation):
|
|||||||
Add the various storage server-related attributes needed by a
|
Add the various storage server-related attributes needed by a
|
||||||
``Tub``-specific ``_FoolscapOrHttps`` subclass.
|
``Tub``-specific ``_FoolscapOrHttps`` subclass.
|
||||||
"""
|
"""
|
||||||
# TODO tub.locationHints will be in the format ["tcp:hostname:port"]
|
|
||||||
# (and maybe some other things we can ignore for now). We also have
|
|
||||||
# access to the certificate. Together, this should be sufficient to
|
|
||||||
# construct NURLs, one per hint. The code for NURls should be
|
|
||||||
# refactored out of http_server.py's build_nurl; that code might want
|
|
||||||
# to skip around for the future when we don't do foolscap, but for now
|
|
||||||
# this module will be main way we set up HTTPS.
|
|
||||||
|
|
||||||
# Tub.myCertificate is a twisted.internet.ssl.PrivateCertificate
|
# Tub.myCertificate is a twisted.internet.ssl.PrivateCertificate
|
||||||
# instance.
|
# instance.
|
||||||
certificate_options = CertificateOptions(
|
certificate_options = CertificateOptions(
|
||||||
|
@ -10,6 +10,7 @@ from base64 import b64decode
|
|||||||
import binascii
|
import binascii
|
||||||
from tempfile import TemporaryFile
|
from tempfile import TemporaryFile
|
||||||
|
|
||||||
|
from cryptography.x509 import Certificate
|
||||||
from zope.interface import implementer
|
from zope.interface import implementer
|
||||||
from klein import Klein
|
from klein import Klein
|
||||||
from twisted.web import http
|
from twisted.web import http
|
||||||
@ -843,6 +844,29 @@ class _TLSEndpointWrapper(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_nurl(
|
||||||
|
hostname: str, port: int, swissnum: str, certificate: Certificate
|
||||||
|
) -> DecodedURL:
|
||||||
|
"""
|
||||||
|
Construct a HTTPS NURL, given the hostname, port, server swissnum, and x509
|
||||||
|
certificate for the server. Clients can then connect to the server using
|
||||||
|
this NURL.
|
||||||
|
"""
|
||||||
|
return DecodedURL().replace(
|
||||||
|
fragment="v=1", # how we know this NURL is HTTP-based (i.e. not Foolscap)
|
||||||
|
host=hostname,
|
||||||
|
port=port,
|
||||||
|
path=(swissnum,),
|
||||||
|
userinfo=(
|
||||||
|
str(
|
||||||
|
get_spki_hash(certificate),
|
||||||
|
"ascii",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
scheme="pb",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def listen_tls(
|
def listen_tls(
|
||||||
server: HTTPServer,
|
server: HTTPServer,
|
||||||
hostname: str,
|
hostname: str,
|
||||||
@ -862,22 +886,14 @@ def listen_tls(
|
|||||||
"""
|
"""
|
||||||
endpoint = _TLSEndpointWrapper.from_paths(endpoint, private_key_path, cert_path)
|
endpoint = _TLSEndpointWrapper.from_paths(endpoint, private_key_path, cert_path)
|
||||||
|
|
||||||
def build_nurl(listening_port: IListeningPort) -> DecodedURL:
|
def get_nurl(listening_port: IListeningPort) -> DecodedURL:
|
||||||
nurl = DecodedURL().replace(
|
return build_nurl(
|
||||||
fragment="v=1", # how we know this NURL is HTTP-based (i.e. not Foolscap)
|
hostname,
|
||||||
host=hostname,
|
listening_port.getHost().port,
|
||||||
port=listening_port.getHost().port,
|
str(server._swissnum, "ascii"),
|
||||||
path=(str(server._swissnum, "ascii"),),
|
load_pem_x509_certificate(cert_path.getContent()),
|
||||||
userinfo=(
|
|
||||||
str(
|
|
||||||
get_spki_hash(load_pem_x509_certificate(cert_path.getContent())),
|
|
||||||
"ascii",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
scheme="pb",
|
|
||||||
)
|
)
|
||||||
return nurl
|
|
||||||
|
|
||||||
return endpoint.listen(Site(server.get_resource())).addCallback(
|
return endpoint.listen(Site(server.get_resource())).addCallback(
|
||||||
lambda listening_port: (build_nurl(listening_port), listening_port)
|
lambda listening_port: (get_nurl(listening_port), listening_port)
|
||||||
)
|
)
|
||||||
|
@ -1084,40 +1084,17 @@ class _FoolscapMixin(_SharedMixin):
|
|||||||
class _HTTPMixin(_SharedMixin):
|
class _HTTPMixin(_SharedMixin):
|
||||||
"""Run tests on the HTTP version of ``IStorageServer``."""
|
"""Run tests on the HTTP version of ``IStorageServer``."""
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self._port_assigner = SameProcessStreamEndpointAssigner()
|
|
||||||
self._port_assigner.setUp()
|
|
||||||
self.addCleanup(self._port_assigner.tearDown)
|
|
||||||
return _SharedMixin.setUp(self)
|
|
||||||
|
|
||||||
@inlineCallbacks
|
|
||||||
def _get_istorage_server(self):
|
def _get_istorage_server(self):
|
||||||
swissnum = b"1234"
|
nurl = self.clients[0].storage_nurls[0]
|
||||||
http_storage_server = HTTPServer(self.server, swissnum)
|
|
||||||
|
|
||||||
# Listen on randomly assigned port, using self-signed cert:
|
|
||||||
private_key = generate_private_key()
|
|
||||||
certificate = generate_certificate(private_key)
|
|
||||||
_, endpoint_string = self._port_assigner.assign(reactor)
|
|
||||||
nurl, listening_port = yield listen_tls(
|
|
||||||
http_storage_server,
|
|
||||||
"127.0.0.1",
|
|
||||||
serverFromString(reactor, endpoint_string),
|
|
||||||
private_key_to_file(FilePath(self.mktemp()), private_key),
|
|
||||||
cert_to_file(FilePath(self.mktemp()), certificate),
|
|
||||||
)
|
|
||||||
self.addCleanup(listening_port.stopListening)
|
|
||||||
|
|
||||||
# Create HTTP client with non-persistent connections, so we don't leak
|
# Create HTTP client with non-persistent connections, so we don't leak
|
||||||
# state across tests:
|
# state across tests:
|
||||||
returnValue(
|
client: IStorageServer = _HTTPStorageServer.from_http_client(
|
||||||
_HTTPStorageServer.from_http_client(
|
|
||||||
StorageClient.from_nurl(nurl, reactor, persistent=False)
|
StorageClient.from_nurl(nurl, reactor, persistent=False)
|
||||||
)
|
)
|
||||||
)
|
self.assertTrue(IStorageServer.providedBy(client))
|
||||||
|
|
||||||
# Eventually should also:
|
return succeed(client)
|
||||||
# self.assertTrue(IStorageServer.providedBy(client))
|
|
||||||
|
|
||||||
|
|
||||||
class FoolscapSharedAPIsTests(
|
class FoolscapSharedAPIsTests(
|
||||||
|
Reference in New Issue
Block a user