from zope.interface import Interface
from foolscap.api import StringConstraint, TupleOf, SetOf, DictOf, Any, \
- RemoteInterface
+ RemoteInterface, Referenceable
+from old import RIIntroducerSubscriberClient_v1
FURL = StringConstraint(1000)
+# old introducer protocol (v1):
+#
# Announcements are (FURL, service_name, remoteinterface_name,
# nickname, my_version, oldest_supported)
# the (FURL, service_name, remoteinterface_name) refer to the service being
# incompatible peer. The second goal is to enable the development of
# backwards-compatibility code.
-Announcement = TupleOf(FURL, str, str,
- str, str, str)
+Announcement_v1 = TupleOf(FURL, str, str,
+ str, str, str)
-class RIIntroducerSubscriberClient(RemoteInterface):
- __remote_name__ = "RIIntroducerSubscriberClient.tahoe.allmydata.com"
+# v2 protocol over foolscap: Announcements are 3-tuples of (bytes, str, str)
+# or (bytes, none, none)
+Announcement_v2 = Any()
- def announce(announcements=SetOf(Announcement)):
+class RIIntroducerSubscriberClient_v2(RemoteInterface):
+ __remote_name__ = "RIIntroducerSubscriberClient_v2.tahoe.allmydata.com"
+
+ def announce_v2(announcements=SetOf(Announcement_v2)):
"""I accept announcements from the publisher."""
return None
"""
return None
-# When Foolscap can handle multiple interfaces (Foolscap#17), the
-# full-powered introducer will implement both RIIntroducerPublisher and
-# RIIntroducerSubscriberService. Until then, we define
-# RIIntroducerPublisherAndSubscriberService as a combination of the two, and
-# make everybody use that.
+SubscriberInfo = DictOf(str, Any())
-class RIIntroducerPublisher(RemoteInterface):
+class RIIntroducerPublisherAndSubscriberService_v2(RemoteInterface):
"""To publish a service to the world, connect to me and give me your
- announcement message. I will deliver a copy to all connected subscribers."""
- __remote_name__ = "RIIntroducerPublisher.tahoe.allmydata.com"
-
- def publish(announcement=Announcement):
- # canary?
- return None
-
-class RIIntroducerSubscriberService(RemoteInterface):
- __remote_name__ = "RIIntroducerSubscriberService.tahoe.allmydata.com"
-
- def subscribe(subscriber=RIIntroducerSubscriberClient, service_name=str):
- """Give me a subscriber reference, and I will call its new_peers()
- method will any announcements that match the desired service name. I
- will ignore duplicate subscriptions.
- """
- return None
-
-class RIIntroducerPublisherAndSubscriberService(RemoteInterface):
- __remote_name__ = "RIIntroducerPublisherAndSubscriberService.tahoe.allmydata.com"
+ announcement message. I will deliver a copy to all connected subscribers.
+ To hear about services, connect to me and subscribe to a specific
+ service_name."""
+ __remote_name__ = "RIIntroducerPublisherAndSubscriberService_v2.tahoe.allmydata.com"
def get_version():
return DictOf(str, Any())
- def publish(announcement=Announcement):
+ def publish(announcement=Announcement_v1):
+ return None
+ def publish_v2(announcement=Announcement_v2, canary=Referenceable):
return None
- def subscribe(subscriber=RIIntroducerSubscriberClient, service_name=str):
+ def subscribe(subscriber=RIIntroducerSubscriberClient_v1, service_name=str):
+ return None
+ def subscribe_v2(subscriber=RIIntroducerSubscriberClient_v2,
+ service_name=str, subscriber_info=SubscriberInfo):
+ """Give me a subscriber reference, and I will call its announce_v2()
+ method with any announcements that match the desired service name. I
+ will ignore duplicate subscriptions. The subscriber_info dictionary
+ tells me about the subscriber, and is used for diagnostic/status
+ displays."""
return None
class IIntroducerClient(Interface):
publish their services to the rest of the world, and I help them learn
about services available on other nodes."""
- def publish(furl, service_name, remoteinterface_name):
- """Once you call this, I will tell the world that the Referenceable
- available at FURL is available to provide a service named
- SERVICE_NAME. The precise definition of the service being provided is
- identified by the Foolscap 'remote interface name' in the last
- parameter: this is supposed to be a globally-unique string that
- identifies the RemoteInterface that is implemented."""
+ def publish(service_name, ann, signing_key=None):
+ """Publish the given announcement dictionary (which must be
+ JSON-serializable), plus some additional keys, to the world.
+
+ Each announcement is characterized by a (service_name, serverid)
+ pair. When the server sees two announcements with the same pair, the
+ later one will replace the earlier one. The serverid is derived from
+ the signing_key, if present, otherwise it is derived from the
+ 'anonymous-storage-FURL' key.
+
+ If signing_key= is set to an instance of SigningKey, it will be
+ used to sign the announcement."""
def subscribe_to(service_name, callback, *args, **kwargs):
"""Call this if you will eventually want to use services with the
given SERVICE_NAME. This will prompt me to subscribe to announcements
of those services. Your callback will be invoked with at least two
- arguments: a serverid (binary string), and an announcement
- dictionary, followed by any additional callback args/kwargs you give
- me. I will run your callback for both new announcements and for
+ arguments: a pubkey and an announcement dictionary, followed by any
+ additional callback args/kwargs you gave me. The pubkey will be None
+ unless the announcement was signed by the corresponding pubkey, in
+ which case it will be a printable string like 'v0-base32..'.
+
+ I will run your callback for both new announcements and for
announcements that have changed, but you must be prepared to tolerate
duplicates.
- The announcement dictionary that I give you will have the following
- keys:
+ The announcement that I give you comes from some other client. It
+ will be a JSON-serializable dictionary which (by convention) is
+ expected to have at least the following keys:
version: 0
- service-name: str('storage')
-
- FURL: str(furl)
- remoteinterface-name: str(ri_name)
nickname: unicode
app-versions: {}
my-version: str
oldest-supported: str
- Note that app-version will be an empty dictionary until #466 is done
- and both the introducer and the remote client have been upgraded. For
- current (native) server types, the serverid will always be equal to
- the binary form of the FURL's tubid.
+ service-name: str('storage')
+ anonymous-storage-FURL: str(furl)
+
+ Note that app-version will be an empty dictionary if either the
+ publishing client or the Introducer are running older code.
"""
def connected_to_introducer():