Esempio n. 1
0
    def _callFinished(self, res, delivery):
        reqID = delivery.reqID
        if reqID == 0:
            return
        methodSchema = delivery.methodSchema
        assert self.activeLocalCalls[reqID]
        methodName = None
        if methodSchema:
            methodName = methodSchema.name
            try:
                methodSchema.checkResults(res, False)  # may raise Violation
            except Violation as v:
                v.prependLocation("in return value of %s.%s" %
                                  (delivery.obj, methodSchema.name))
                raise

        answer = call.AnswerSlicer(reqID, res, methodName)
        # once the answer has started transmitting, any exceptions must be
        # logged and dropped, and not turned into an Error to be sent.
        try:
            self.send(answer)
            # TODO: .send should return a Deferred that fires when the last
            # byte has been queued, and we should delete the local note then
        except:
            f = failure.Failure()
            log.msg("Broker._callfinished unable to send",
                    facility="foolscap",
                    level=log.UNUSUAL,
                    failure=f)
        del self.activeLocalCalls[reqID]
Esempio n. 2
0
 def connectionLost(self, why):
     tubid = "?"
     if self.remote_tubref:
         tubid = self.remote_tubref.getShortTubID()
     log.msg("connection to %s lost" % tubid, facility="foolscap.connection")
     banana.Banana.connectionLost(self, why)
     self.finish(why)
Esempio n. 3
0
        def _created(n):
            d = defer.succeed(None)
            d.addCallback(lambda res: n.get_servermap(MODE_READ))

            def _got_smap1(smap):
                # stash the old state of the file
                self.old_map = smap

            d.addCallback(_got_smap1)
            # then modify the file, leaving the old map untouched
            d.addCallback(lambda res: log.msg("starting winning write"))
            d.addCallback(lambda res: n.overwrite(MutableData("contents 2")))
            # now attempt to retrieve the old version with the old servermap.
            # This will look like someone has changed the file since we
            # updated the servermap.
            d.addCallback(lambda res: log.msg("starting doomed read"))
            d.addCallback(lambda res: self.shouldFail(
                NotEnoughSharesError,
                "test_retrieve_surprise",
                "ran out of servers: have 0 of 1",
                n.download_version,
                self.old_map,
                self.old_map.best_recoverable_version(),
            ))
            return d
Esempio n. 4
0
def create_main_tub(config,
                    tub_options,
                    default_connection_handlers,
                    foolscap_connection_handlers,
                    i2p_provider,
                    tor_provider,
                    handler_overrides={},
                    cert_filename="node.pem"):
    """
    Creates a 'main' Foolscap Tub, typically for use as the top-level
    access point for a running Node.

    :param Config: a `_Config` instance

    :param dict tub_options: any options to change in the tub

    :param default_connection_handlers: default Foolscap connection
        handlers

    :param foolscap_connection_handlers: Foolscap connection
        handlers for this tub

    :param i2p_provider: None, or a _Provider instance if I2P is
        installed.

    :param tor_provider: None, or a _Provider instance if txtorcon +
        Tor are installed.
    """
    portlocation = _tub_portlocation(config)

    certfile = config.get_private_path(
        "node.pem")  # FIXME? "node.pem" was the CERTFILE option/thing
    tub = create_tub(tub_options,
                     default_connection_handlers,
                     foolscap_connection_handlers,
                     handler_overrides=handler_overrides,
                     certFile=certfile)

    if portlocation:
        tubport, location = portlocation
        for port in tubport.split(","):
            if port == "listen:i2p":
                # the I2P provider will read its section of tahoe.cfg and
                # return either a fully-formed Endpoint, or a descriptor
                # that will create one, so we don't have to stuff all the
                # options into the tub.port string (which would need a lot
                # of escaping)
                port_or_endpoint = i2p_provider.get_listener()
            elif port == "listen:tor":
                port_or_endpoint = tor_provider.get_listener()
            else:
                port_or_endpoint = port
            tub.listenOn(port_or_endpoint)
        tub.setLocation(location)
        log.msg("Tub location set to %s" % (location, ))
        # the Tub is now ready for tub.registerReference()
    else:
        log.msg("Tub is not listening")

    return tub
Esempio n. 5
0
    def _getReference(self, sturdyOrURL):
        if isinstance(sturdyOrURL, SturdyRef):
            sturdy = sturdyOrURL
        else:
            sturdy = SturdyRef(sturdyOrURL)
        # pb->pb: ok, requires crypto
        # pbu->pb: ok, requires crypto
        # pbu->pbu: ok
        # pb->pbu: ok, requires crypto
        if sturdy.encrypted and not crypto_available:
            e = BananaError("crypto for PB is not available, "
                            "we cannot handle encrypted PB-URLs like %s" %
                            sturdy.getURL())
            return defer.fail(e)

        if not self.running:
            # queue their request for service once the Tub actually starts
            log.msg(
                "Tub.getReference(%s) queued until Tub.startService called" %
                sturdy,
                facility="foolscap.tub")
            d = defer.Deferred()
            self._pending_getReferences.append((d, sturdy))
            return d

        name = sturdy.name
        d = self.getBrokerForTubRef(sturdy.getTubRef())
        d.addCallback(lambda b: b.getYourReferenceByName(name))
        return d
Esempio n. 6
0
    def _getReference(self, sturdyOrURL):
        if isinstance(sturdyOrURL, SturdyRef):
            sturdy = sturdyOrURL
        else:
            sturdy = SturdyRef(sturdyOrURL)
        # pb->pb: ok, requires crypto
        # pbu->pb: ok, requires crypto
        # pbu->pbu: ok
        # pb->pbu: ok, requires crypto
        if sturdy.encrypted and not crypto_available:
            e = BananaError("crypto for PB is not available, "
                            "we cannot handle encrypted PB-URLs like %s"
                            % sturdy.getURL())
            return defer.fail(e)

        if not self.running:
            # queue their request for service once the Tub actually starts
            log.msg("Tub.getReference(%s) queued until Tub.startService called"
                    % sturdy, facility="foolscap.tub")
            d = defer.Deferred()
            self._pending_getReferences.append((d, sturdy))
            return d

        name = sturdy.name
        d = self.getBrokerForTubRef(sturdy.getTubRef())
        d.addCallback(lambda b: b.getYourReferenceByName(name))
        return d
Esempio n. 7
0
        def _merge(res):
            log.msg("merging sharelists")
            # we merge the shares from the two sets, leaving each shnum in
            # its original location, but using a share from set1 or set2
            # according to the following sequence:
            #
            #  4-of-9  a  s2
            #  4-of-9  b  s2
            #  4-of-7  c   s3
            #  4-of-9  d  s2
            #  3-of-9  e s1
            #  3-of-9  f s1
            #  3-of-9  g s1
            #  4-of-9  h  s2
            #
            # so that neither form can be recovered until fetch [f], at which
            # point version-s1 (the 3-of-10 form) should be recoverable. If
            # the implementation latches on to the first version it sees,
            # then s2 will be recoverable at fetch [g].

            # Later, when we implement code that handles multiple versions,
            # we can use this framework to assert that all recoverable
            # versions are retrieved, and test that 'epsilon' does its job

            places = [2, 2, 3, 2, 1, 1, 1, 2]

            sharemap = {}
            sb = self._storage_broker

            for peerid in sorted(sb.get_all_serverids()):
                for shnum in self._shares1.get(peerid, {}):
                    if shnum < len(places):
                        which = places[shnum]
                    else:
                        which = "x"
                    self._storage._peers[peerid] = peers = {}
                    in_1 = shnum in self._shares1[peerid]
                    in_2 = shnum in self._shares2.get(peerid, {})
                    in_3 = shnum in self._shares3.get(peerid, {})
                    if which == 1:
                        if in_1:
                            peers[shnum] = self._shares1[peerid][shnum]
                            sharemap[shnum] = peerid
                    elif which == 2:
                        if in_2:
                            peers[shnum] = self._shares2[peerid][shnum]
                            sharemap[shnum] = peerid
                    elif which == 3:
                        if in_3:
                            peers[shnum] = self._shares3[peerid][shnum]
                            sharemap[shnum] = peerid

            # we don't bother placing any other shares
            # now sort the sequence so that share 0 is returned first
            new_sequence = [
                sharemap[shnum] for shnum in sorted(sharemap.keys())
            ]
            self._storage._sequence = new_sequence
            log.msg("merge done")
Esempio n. 8
0
 def complete(self, res):
     if self.broker:
         self.broker.removeRequest(self)
     if self.active:
         self.active = False
         self.deferred.callback(res)
     else:
         log.msg("PendingRequest.complete called on an inactive request")
Esempio n. 9
0
 def complete(self, res):
     if self.broker:
         self.broker.removeRequest(self)
     if self.active:
         self.active = False
         self.deferred.callback(res)
     else:
         log.msg("PendingRequest.complete called on an inactive request")
Esempio n. 10
0
 def connectionLost(self, why):
     tubid = "?"
     if self.remote_tubref:
         tubid = self.remote_tubref.getShortTubID()
     log.msg("connection to %s lost" % tubid,
             facility="foolscap.connection")
     banana.Banana.connectionLost(self, why)
     self.finish(why)
Esempio n. 11
0
 def _got_smap1(smap):
     # stash the old state of the file
     self.old_map = smap
     # now shut down one of the servers
     peer0 = list(smap.make_sharemap()[0])[0].get_serverid()
     self.g.remove_server(peer0)
     # then modify the file, leaving the old map untouched
     log.msg("starting winning write")
     return n.overwrite(MutableData("contents 2"))
        def _merge(res):
            log.msg("merging sharelists")
            # we merge the shares from the two sets, leaving each shnum in
            # its original location, but using a share from set1 or set2
            # according to the following sequence:
            #
            #  4-of-9  a  s2
            #  4-of-9  b  s2
            #  4-of-7  c   s3
            #  4-of-9  d  s2
            #  3-of-9  e s1
            #  3-of-9  f s1
            #  3-of-9  g s1
            #  4-of-9  h  s2
            #
            # so that neither form can be recovered until fetch [f], at which
            # point version-s1 (the 3-of-10 form) should be recoverable. If
            # the implementation latches on to the first version it sees,
            # then s2 will be recoverable at fetch [g].

            # Later, when we implement code that handles multiple versions,
            # we can use this framework to assert that all recoverable
            # versions are retrieved, and test that 'epsilon' does its job

            places = [2, 2, 3, 2, 1, 1, 1, 2]

            sharemap = {}
            sb = self._storage_broker

            for peerid in sorted(sb.get_all_serverids()):
                for shnum in self._shares1.get(peerid, {}):
                    if shnum < len(places):
                        which = places[shnum]
                    else:
                        which = "x"
                    self._storage._peers[peerid] = peers = {}
                    in_1 = shnum in self._shares1[peerid]
                    in_2 = shnum in self._shares2.get(peerid, {})
                    in_3 = shnum in self._shares3.get(peerid, {})
                    if which == 1:
                        if in_1:
                            peers[shnum] = self._shares1[peerid][shnum]
                            sharemap[shnum] = peerid
                    elif which == 2:
                        if in_2:
                            peers[shnum] = self._shares2[peerid][shnum]
                            sharemap[shnum] = peerid
                    elif which == 3:
                        if in_3:
                            peers[shnum] = self._shares3[peerid][shnum]
                            sharemap[shnum] = peerid

            # we don't bother placing any other shares
            # now sort the sequence so that share 0 is returned first
            new_sequence = [sharemap[shnum] for shnum in sorted(sharemap.keys())]
            self._storage._sequence = new_sequence
            log.msg("merge done")
Esempio n. 13
0
def create_main_tub(config,
                    tub_options,
                    default_connection_handlers,
                    foolscap_connection_handlers,
                    i2p_provider,
                    tor_provider,
                    handler_overrides={},
                    cert_filename="node.pem"):
    """
    Creates a 'main' Foolscap Tub, typically for use as the top-level
    access point for a running Node.

    :param Config: a `_Config` instance

    :param dict tub_options: any options to change in the tub

    :param default_connection_handlers: default Foolscap connection
        handlers

    :param foolscap_connection_handlers: Foolscap connection
        handlers for this tub

    :param i2p_provider: None, or a _Provider instance if I2P is
        installed.

    :param tor_provider: None, or a _Provider instance if txtorcon +
        Tor are installed.
    """
    portlocation = _tub_portlocation(
        config,
        iputil.get_local_addresses_sync,
        iputil.allocate_tcp_port,
    )

    # FIXME? "node.pem" was the CERTFILE option/thing
    certfile = config.get_private_path("node.pem")

    tub = create_tub(
        tub_options,
        default_connection_handlers,
        foolscap_connection_handlers,
        handler_overrides=handler_overrides,
        certFile=certfile,
    )
    if portlocation is None:
        log.msg("Tub is not listening")
    else:
        tubport, location = portlocation
        tub_listen_on(
            i2p_provider,
            tor_provider,
            tub,
            tubport,
            location,
        )
        log.msg("Tub location set to %s" % (location, ))
    return tub
Esempio n. 14
0
 def _got_smap1(smap):
     # stash the old state of the file
     self.old_map = smap
     # now shut down one of the servers
     peer0 = list(smap.make_sharemap()[0])[0].get_serverid()
     self.g.remove_server(peer0)
     # then modify the file, leaving the old map untouched
     log.msg("starting winning write")
     return n.overwrite(MutableData(b"contents 2"))
Esempio n. 15
0
def create_control_tub():
    """
    Creates a Foolscap Tub for use by the control port. This is a
    localhost-only ephemeral Tub, with no control over the listening
    port or location
    """
    control_tub = Tub()
    portnum = iputil.listenOnUnused(control_tub)
    log.msg("Control Tub location set to 127.0.0.1:%s" % (portnum, ))
    return control_tub
Esempio n. 16
0
def create_control_tub():
    """
    Creates a Foolscap Tub for use by the control port. This is a
    localhost-only ephemeral Tub, with no control over the listening
    port or location
    """
    control_tub = Tub()
    portnum = iputil.listenOnUnused(control_tub)
    log.msg("Control Tub location set to 127.0.0.1:%s" % (portnum,))
    return control_tub
Esempio n. 17
0
 def logFailure(self, f):
     # called if tub.logLocalFailures is True
     my_short_tubid = "??"
     if self.broker.tub:  # for tests
         my_short_tubid = self.broker.tub.getShortTubID()
     their_short_tubid = "<???>"
     if self.broker.remote_tubref:
         their_short_tubid = self.broker.remote_tubref.getShortTubID()
     lp = log.msg("an inbound callRemote that we [%s] executed (on behalf "
                  "of someone else, TubID %s) failed" %
                  (my_short_tubid, their_short_tubid),
                  level=log.UNUSUAL)
     if self.interface:
         methname = self.interface.getName() + "." + self.methodname
     else:
         methname = self.methodname
     log.msg(" reqID=%d, rref=%s, methname=%s" %
             (self.reqID, self.obj, methname),
             level=log.NOISY,
             parent=lp)
     log.msg(" args=%s" % (self.allargs.args, ), level=log.NOISY, parent=lp)
     log.msg(" kwargs=%s" % (self.allargs.kwargs, ),
             level=log.NOISY,
             parent=lp)
     #if isinstance(f.type, str):
     #    stack = "getTraceback() not available for string exceptions\n"
     #else:
     #    stack = f.getTraceback()
     # TODO: trim stack to everything below Broker._doCall
     #stack = "LOCAL: " + stack.replace("\n", "\nLOCAL: ")
     log.msg(" the LOCAL failure was:",
             failure=f,
             level=log.NOISY,
             parent=lp)
Esempio n. 18
0
 def logFailure(self, f):
     # called if tub.logLocalFailures is True
     my_short_tubid = "??"
     if self.broker.tub: # for tests
         my_short_tubid = self.broker.tub.getShortTubID()
     their_short_tubid = "<unauth>"
     if self.broker.remote_tubref:
         their_short_tubid = self.broker.remote_tubref.getShortTubID()
     lp = log.msg("an inbound callRemote that we [%s] executed (on behalf "
                  "of someone else, TubID %s) failed"
                  % (my_short_tubid, their_short_tubid),
                  level=log.UNUSUAL)
     if self.interface:
         methname = self.interface.getName() + "." + self.methodname
     else:
         methname = self.methodname
     log.msg(" reqID=%d, rref=%s, methname=%s" %
             (self.reqID, self.obj, methname),
             level=log.NOISY, parent=lp)
     log.msg(" args=%s" % (self.allargs.args,), level=log.NOISY, parent=lp)
     log.msg(" kwargs=%s" % (self.allargs.kwargs,),
             level=log.NOISY, parent=lp)
     #if isinstance(f.type, str):
     #    stack = "getTraceback() not available for string exceptions\n"
     #else:
     #    stack = f.getTraceback()
     # TODO: trim stack to everything below Broker._doCall
     #stack = "LOCAL: " + stack.replace("\n", "\nLOCAL: ")
     log.msg(" the LOCAL failure was:", failure=f,
             level=log.NOISY, parent=lp)
Esempio n. 19
0
 def start(self, count):
     if self.debug:
         log.msg("%s.start: %s" % (self, count))
     self.numargs = None
     self.args = []
     self.kwargs = {}
     self.argname = None
     self.argConstraint = None
     self.num_unreferenceable_children = 0
     self._all_children_are_referenceable_d = None
     self._ready_deferreds = []
     self.closed = False
Esempio n. 20
0
 def start(self, count):
     if self.debug:
         log.msg("%s.start: %s" % (self, count))
     self.numargs = None
     self.args = []
     self.kwargs = {}
     self.argname = None
     self.argConstraint = None
     self.num_unreferenceable_children = 0
     self._all_children_are_referenceable_d = None
     self._ready_deferreds = []
     self.closed = False
Esempio n. 21
0
def create_main_tub(config, tub_options,
                    default_connection_handlers, foolscap_connection_handlers,
                    i2p_provider, tor_provider,
                    handler_overrides={}, cert_filename="node.pem"):
    """
    Creates a 'main' Foolscap Tub, typically for use as the top-level
    access point for a running Node.

    :param Config: a `_Config` instance

    :param dict tub_options: any options to change in the tub

    :param default_connection_handlers: default Foolscap connection
        handlers

    :param foolscap_connection_handlers: Foolscap connection
        handlers for this tub

    :param i2p_provider: None, or a _Provider instance if I2P is
        installed.

    :param tor_provider: None, or a _Provider instance if txtorcon +
        Tor are installed.
    """
    portlocation = _tub_portlocation(config)

    certfile = config.get_private_path("node.pem")  # FIXME? "node.pem" was the CERTFILE option/thing
    tub = create_tub(tub_options, default_connection_handlers, foolscap_connection_handlers,
                     handler_overrides=handler_overrides, certFile=certfile)

    if portlocation:
        tubport, location = portlocation
        for port in tubport.split(","):
            if port == "listen:i2p":
                # the I2P provider will read its section of tahoe.cfg and
                # return either a fully-formed Endpoint, or a descriptor
                # that will create one, so we don't have to stuff all the
                # options into the tub.port string (which would need a lot
                # of escaping)
                port_or_endpoint = i2p_provider.get_listener()
            elif port == "listen:tor":
                port_or_endpoint = tor_provider.get_listener()
            else:
                port_or_endpoint = port
            tub.listenOn(port_or_endpoint)
        tub.setLocation(location)
        log.msg("Tub location set to %s" % (location,))
        # the Tub is now ready for tub.registerReference()
    else:
        log.msg("Tub is not listening")

    return tub
Esempio n. 22
0
def create_control_tub():
    """
    Creates a Foolscap Tub for use by the control port. This is a
    localhost-only ephemeral Tub, with no control over the listening
    port or location
    """
    control_tub = Tub()
    portnum = iputil.allocate_tcp_port()
    port = "tcp:%d:interface=127.0.0.1" % portnum
    location = "tcp:127.0.0.1:%d" % portnum
    control_tub.listenOn(port)
    control_tub.setLocation(location)
    log.msg("Control Tub location set to %s" % (location,))
    return control_tub
Esempio n. 23
0
def create_connection_handlers(reactor, config, i2p_provider, tor_provider):
    """
    :returns: 2-tuple of default_connection_handlers, foolscap_connection_handlers
    """
    reveal_ip = config.get_config("node", "reveal-IP-address", True, boolean=True)

    # We store handlers for everything. None means we were unable to
    # create that handler, so hints which want it will be ignored.
    handlers = foolscap_connection_handlers = {
        "tcp": _make_tcp_handler(),
        "tor": tor_provider.get_tor_handler(),
        "i2p": i2p_provider.get_i2p_handler(),
        }
    log.msg(
        format="built Foolscap connection handlers for: %(known_handlers)s",
        known_handlers=sorted([k for k,v in handlers.items() if v]),
        facility="tahoe.node",
        umid="PuLh8g",
    )

    # then we remember the default mappings from tahoe.cfg
    default_connection_handlers = {"tor": "tor", "i2p": "i2p"}
    tcp_handler_name = config.get_config("connections", "tcp", "tcp").lower()
    if tcp_handler_name == "disabled":
        default_connection_handlers["tcp"] = None
    else:
        if tcp_handler_name not in handlers:
            raise ValueError(
                "'tahoe.cfg [connections] tcp=' uses "
                "unknown handler type '{}'".format(
                    tcp_handler_name
                )
            )
        if not handlers[tcp_handler_name]:
            raise ValueError(
                "'tahoe.cfg [connections] tcp=' uses "
                "unavailable/unimportable handler type '{}'. "
                "Please pip install tahoe-lafs[{}] to fix.".format(
                    tcp_handler_name,
                    tcp_handler_name,
                )
            )
        default_connection_handlers["tcp"] = tcp_handler_name

    if not reveal_ip:
        if default_connection_handlers.get("tcp") == "tcp":
            raise PrivacyError("tcp = tcp, must be set to 'tor' or 'disabled'")
    return default_connection_handlers, foolscap_connection_handlers
Esempio n. 24
0
def create_connection_handlers(reactor, config, i2p_provider, tor_provider):
    """
    :returns: 2-tuple of default_connection_handlers, foolscap_connection_handlers
    """
    reveal_ip = config.get_config("node", "reveal-IP-address", True, boolean=True)

    # We store handlers for everything. None means we were unable to
    # create that handler, so hints which want it will be ignored.
    handlers = foolscap_connection_handlers = {
        "tcp": _make_tcp_handler(),
        "tor": tor_provider.get_tor_handler(),
        "i2p": i2p_provider.get_i2p_handler(),
        }
    log.msg(
        format="built Foolscap connection handlers for: %(known_handlers)s",
        known_handlers=sorted([k for k,v in handlers.items() if v]),
        facility="tahoe.node",
        umid="PuLh8g",
    )

    # then we remember the default mappings from tahoe.cfg
    default_connection_handlers = {"tor": "tor", "i2p": "i2p"}
    tcp_handler_name = config.get_config("connections", "tcp", "tcp").lower()
    if tcp_handler_name == "disabled":
        default_connection_handlers["tcp"] = None
    else:
        if tcp_handler_name not in handlers:
            raise ValueError(
                "'tahoe.cfg [connections] tcp=' uses "
                "unknown handler type '{}'".format(
                    tcp_handler_name
                )
            )
        if not handlers[tcp_handler_name]:
            raise ValueError(
                "'tahoe.cfg [connections] tcp=' uses "
                "unavailable/unimportable handler type '{}'. "
                "Please pip install tahoe-lafs[{}] to fix.".format(
                    tcp_handler_name,
                    tcp_handler_name,
                )
            )
        default_connection_handlers["tcp"] = tcp_handler_name

    if not reveal_ip:
        if default_connection_handlers.get("tcp") == "tcp":
            raise PrivacyError("tcp = tcp, must be set to 'tor' or 'disabled'")
    return default_connection_handlers, foolscap_connection_handlers
Esempio n. 25
0
    def __init__(self, parent, tubref, connectionPlugins):
        self._logparent = log.msg(
            format="TubConnector created from " "%(fromtubid)s to %(totubid)s",
            fromtubid=parent.tubID,
            totubid=tubref.getTubID(),
            level=OPERATIONAL,
            facility="foolscap.connection",
            umid="pH4QDA",
        )
        self.tub = parent
        self.target = tubref
        self.connectionPlugins = connectionPlugins
        self.remainingLocations = list(self.target.getLocations())
        # attemptedLocations keeps track of where we've already tried to
        # connect, so we don't try them twice, even if they appear in the
        # hints multiple times. this isn't too clever: slight variations of
        # the same hint will fool it, but it should be enough to avoid
        # infinite redirection loops.
        self.attemptedLocations = []

        # pendingConnections contains a Deferred for each endpoint.connect()
        # that has started (but not yet established) a connection. We keep
        # track of these so we can shut them down (using d.cancel()) when we
        # stop connecting (either because one of the other connections
        # succeeded, or because someone told us to give up).
        self.pendingConnections = set()

        # self.pendingNegotiations maps Negotiation instances (connected but
        # not finished negotiation) to the hint that got us the connection.
        # We track these so we can abandon the negotiation.
        self.pendingNegotiations = {}
Esempio n. 26
0
    def _getReference(self, sturdyOrURL):
        if isinstance(sturdyOrURL, SturdyRef):
            sturdy = sturdyOrURL
        else:
            sturdy = SturdyRef(sturdyOrURL)

        if not self.running:
            # queue their request for service once the Tub actually starts
            log.msg("Tub.getReference(%s) queued until Tub.startService called"
                    % sturdy, facility="foolscap.tub")
            d = defer.Deferred()
            self._pending_getReferences.append((d, sturdy))
            return d

        name = sturdy.name
        d = self.getBrokerForTubRef(sturdy.getTubRef())
        d.addCallback(lambda b: b.getYourReferenceByName(name))
        return d
Esempio n. 27
0
    def _getReference(self, sturdyOrURL):
        if isinstance(sturdyOrURL, SturdyRef):
            sturdy = sturdyOrURL
        else:
            sturdy = SturdyRef(sturdyOrURL)

        if not self.running:
            # queue their request for service once the Tub actually starts
            log.msg("Tub.getReference(%s) queued until Tub.startService called"
                    % sturdy, facility="foolscap.tub")
            d = defer.Deferred()
            self._pending_getReferences.append((d, sturdy))
            return d

        name = sturdy.name
        d = self.getBrokerForTubRef(sturdy.getTubRef())
        d.addCallback(lambda b: b.getYourReferenceByName(name))
        return d
Esempio n. 28
0
 def receiveClose(self):
     if self.debug:
         log.msg("%s.receiveClose: %s %s %s" %
                 (self, self.closed, self.num_unreferenceable_children,
                  len(self._ready_deferreds)))
     if (self.numargs is None or len(self.args) < self.numargs
             or self.argname is not None):
         raise BananaError("'arguments' sequence ended too early")
     self.closed = True
     dl = []
     if self.num_unreferenceable_children:
         d = self._all_children_are_referenceable_d = defer.Deferred()
         dl.append(d)
     dl.extend(self._ready_deferreds)
     ready_deferred = None
     if dl:
         ready_deferred = AsyncAND(dl)
     return self, ready_deferred
Esempio n. 29
0
    def freeYourReference(self, tracker, count):
        # this is called when the RemoteReference is deleted
        if not self.remote_broker:  # tests do not set this up
            self.freeYourReferenceTracker(None, tracker)
            return
        try:
            rb = self.remote_broker
            # TODO: do we want callRemoteOnly here? is there a way we can
            # avoid wanting to know when the decref has completed? Only if we
            # send the interface list and URL on every occurrence of the
            # my-reference sequence. Either A) we use callRemote("decref")
            # and wait until the ack to free the tracker, or B) we use
            # callRemoteOnly("decref") and free the tracker right away. In
            # case B, the far end has no way to know that we've just freed
            # the tracker and will therefore forget about everything they
            # told us (including the interface list), so they cannot
            # accurately do anything special on the "first" send of this
            # reference. Which means that if we do B, we must either send
            # that extra information on every my-reference sequence, or do
            # without it, or make it optional, or retrieve it separately, or
            # something.

            # rb.callRemoteOnly("decref", clid=tracker.clid, count=count)
            # self.freeYourReferenceTracker('bogus', tracker)
            # return

            d = rb.callRemote("decref", clid=tracker.clid, count=count)

            # if the connection was lost before we can get an ack, we're
            # tearing this down anyway
            def _ignore_loss(f):
                f.trap(DeadReferenceError, *LOST_CONNECTION_ERRORS)
                return None

            d.addErrback(_ignore_loss)
            # once the ack comes back, or if we know we'll never get one,
            # release the tracker
            d.addCallback(self.freeYourReferenceTracker, tracker)
        except:
            f = failure.Failure()
            log.msg("failure during freeRemoteReference",
                    facility="foolscap",
                    level=log.UNUSUAL,
                    failure=f)
Esempio n. 30
0
 def receiveClose(self):
     if self.debug:
         log.msg("%s.receiveClose: %s %s %s" %
                 (self, self.closed, self.num_unreferenceable_children,
                  len(self._ready_deferreds)))
     if (self.numargs is None or
         len(self.args) < self.numargs or
         self.argname is not None):
         raise BananaError("'arguments' sequence ended too early")
     self.closed = True
     dl = []
     if self.num_unreferenceable_children:
         d = self._all_children_are_referenceable_d = defer.Deferred()
         dl.append(d)
     dl.extend(self._ready_deferreds)
     ready_deferred = None
     if dl:
         ready_deferred = AsyncAND(dl)
     return self, ready_deferred
Esempio n. 31
0
 def buildProtocol(self, addr):
     """Return a Broker attached to me (as the service provider).
     """
     lp = log.msg("%s accepting connection from %s" % (self, addr),
                  addr=(addr.host, addr.port),
                  facility="foolscap.listener")
     proto = self.negotiationClass(logparent=lp)
     proto.initServer(self)
     proto.factory = self
     return proto
Esempio n. 32
0
 def buildProtocol(self, addr):
     """Return a Broker attached to me (as the service provider).
     """
     lp = log.msg("%s accepting connection from %s" % (self, addr),
                  addr=(addr.host, addr.port),
                  facility="foolscap.listener")
     proto = self.negotiationClass(logparent=lp)
     proto.initServer(self)
     proto.factory = self
     return proto
Esempio n. 33
0
    def freeYourReference(self, tracker, count):
        # this is called when the RemoteReference is deleted
        if not self.remote_broker: # tests do not set this up
            self.freeYourReferenceTracker(None, tracker)
            return
        try:
            rb = self.remote_broker
            # TODO: do we want callRemoteOnly here? is there a way we can
            # avoid wanting to know when the decref has completed? Only if we
            # send the interface list and URL on every occurrence of the
            # my-reference sequence. Either A) we use callRemote("decref")
            # and wait until the ack to free the tracker, or B) we use
            # callRemoteOnly("decref") and free the tracker right away. In
            # case B, the far end has no way to know that we've just freed
            # the tracker and will therefore forget about everything they
            # told us (including the interface list), so they cannot
            # accurately do anything special on the "first" send of this
            # reference. Which means that if we do B, we must either send
            # that extra information on every my-reference sequence, or do
            # without it, or make it optional, or retrieve it separately, or
            # something.

            # rb.callRemoteOnly("decref", clid=tracker.clid, count=count)
            # self.freeYourReferenceTracker('bogus', tracker)
            # return

            d = rb.callRemote("decref", clid=tracker.clid, count=count)
            # if the connection was lost before we can get an ack, we're
            # tearing this down anyway
            def _ignore_loss(f):
                f.trap(DeadReferenceError,
                       error.ConnectionLost,
                       error.ConnectionDone)
                return None
            d.addErrback(_ignore_loss)
            # once the ack comes back, or if we know we'll never get one,
            # release the tracker
            d.addCallback(self.freeYourReferenceTracker, tracker)
        except:
            f = failure.Failure()
            log.msg("failure during freeRemoteReference", facility="foolscap",
                    level=log.UNUSUAL, failure=f)
Esempio n. 34
0
 def _created(n):
     d = defer.succeed(None)
     d.addCallback(lambda res: n.get_servermap(MODE_WRITE))
     def _got_smap1(smap):
         # stash the old state of the file
         self.old_map = smap
     d.addCallback(_got_smap1)
     # then modify the file, leaving the old map untouched
     d.addCallback(lambda res: log.msg("starting winning write"))
     d.addCallback(lambda res: n.overwrite(MutableData("contents 2")))
     # now attempt to modify the file with the old servermap. This
     # will look just like an uncoordinated write, in which every
     # single share got updated between our mapupdate and our publish
     d.addCallback(lambda res: log.msg("starting doomed write"))
     d.addCallback(lambda res:
                   self.shouldFail(UncoordinatedWriteError,
                                   "test_publish_surprise", None,
                                   n.upload,
                                   MutableData("contents 2a"), self.old_map))
     return d
Esempio n. 35
0
    def updateChild(self, obj, which):
        # one of our arguments has just now become referenceable. Normal
        # types can't trigger this (since the arguments to a method form a
        # top-level serialization domain), but special Unslicers might. For
        # example, the Gift unslicer will eventually provide us with a
        # RemoteReference, but for now all we get is a Deferred as a
        # placeholder.

        if self.debug:
            log.msg("%s.updateChild, [%s] became referenceable: %s" %
                    (self, which, obj))
        if isinstance(which, int):
            self.args[which] = obj
        else:
            self.kwargs[which] = obj
        self.num_unreferenceable_children -= 1
        if self.num_unreferenceable_children == 0:
            if self._all_children_are_referenceable_d:
                self._all_children_are_referenceable_d.callback(None)
        return obj
Esempio n. 36
0
 def log(self, msg, facility=None, parent=None, *args, **kwargs):
     if facility is None:
         facility = self._facility
     if parent is None:
         pmsgid = self._parentmsgid
     if pmsgid is None:
         pmsgid = self._grandparentmsgid
     msgid = log.msg(msg, facility=facility, parent=pmsgid, *args, **kwargs)
     if self._parentmsgid is None:
         self._parentmsgid = msgid
     return msgid
Esempio n. 37
0
    def updateChild(self, obj, which):
        # one of our arguments has just now become referenceable. Normal
        # types can't trigger this (since the arguments to a method form a
        # top-level serialization domain), but special Unslicers might. For
        # example, the Gift unslicer will eventually provide us with a
        # RemoteReference, but for now all we get is a Deferred as a
        # placeholder.

        if self.debug:
            log.msg("%s.updateChild, [%s] became referenceable: %s" %
                    (self, which, obj))
        if isinstance(which, int):
            self.args[which] = obj
        else:
            self.kwargs[which] = obj
        self.num_unreferenceable_children -= 1
        if self.num_unreferenceable_children == 0:
            if self._all_children_are_referenceable_d:
                self._all_children_are_referenceable_d.callback(None)
        return obj
Esempio n. 38
0
 def log(self, msg, facility=None, parent=None, *args, **kwargs):
     if facility is None:
         facility = self._facility
     if parent is None:
         pmsgid = self._parentmsgid
     if pmsgid is None:
         pmsgid = self._grandparentmsgid
     msgid = log.msg(msg, facility=facility, parent=pmsgid, *args, **kwargs)
     if self._parentmsgid is None:
         self._parentmsgid = msgid
     return msgid
Esempio n. 39
0
 def _created(n):
     d = defer.succeed(None)
     d.addCallback(lambda res: n.get_servermap(MODE_WRITE))
     def _got_smap1(smap):
         # stash the old state of the file
         self.old_map = smap
     d.addCallback(_got_smap1)
     # then modify the file, leaving the old map untouched
     d.addCallback(lambda res: log.msg("starting winning write"))
     d.addCallback(lambda res: n.overwrite(MutableData(b"contents 2")))
     # now attempt to modify the file with the old servermap. This
     # will look just like an uncoordinated write, in which every
     # single share got updated between our mapupdate and our publish
     d.addCallback(lambda res: log.msg("starting doomed write"))
     d.addCallback(lambda res:
                   self.shouldFail(UncoordinatedWriteError,
                                   "test_publish_surprise", None,
                                   n.upload,
                                   MutableData(b"contents 2a"), self.old_map))
     return d
Esempio n. 40
0
def create_connection_handlers(config, i2p_provider, tor_provider):
    """
    :returns: 2-tuple of default_connection_handlers, foolscap_connection_handlers
    """
    # We store handlers for everything. None means we were unable to
    # create that handler, so hints which want it will be ignored.
    handlers = {
        "tcp": _make_tcp_handler(),
        "tor": tor_provider.get_client_endpoint(),
        "i2p": i2p_provider.get_client_endpoint(),
    }
    log.msg(
        format="built Foolscap connection handlers for: %(known_handlers)s",
        known_handlers=sorted([k for k, v in handlers.items() if v]),
        facility="tahoe.node",
        umid="PuLh8g",
    )
    return create_default_connection_handlers(
        config,
        handlers,
    ), handlers
Esempio n. 41
0
 def buildProtocol(self, addr):
     """Return a Broker attached to me (as the service provider).
     """
     lp = log.msg("%s accepting connection from %s" % (self, addr),
                  addr=(addr.host, addr.port),
                  facility="foolscap.listener")
     proto = self._negotiationClass(logparent=lp)
     ci = info.ConnectionInfo()
     ci._set_listener_description(self._describe())
     ci._set_listener_status("negotiating")
     proto.initServer(self, ci)
     proto.factory = self
     return proto
Esempio n. 42
0
 def buildProtocol(self, addr):
     """Return a Broker attached to me (as the service provider).
     """
     lp = log.msg("%s accepting connection from %s" % (self, addr),
                  addr=(addr.host, addr.port),
                  facility="foolscap.listener")
     proto = self._negotiationClass(logparent=lp)
     ci = info.ConnectionInfo()
     ci._set_listener_description(self._describe())
     ci._set_listener_status("negotiating")
     proto.initServer(self, ci)
     proto.factory = self
     return proto
Esempio n. 43
0
 def log(self, msg, facility=None, parent=None, *args, **kwargs):
     if facility is None:
         facility = self._facility
     pmsgid = parent
     if pmsgid is None:
         pmsgid = self._parentmsgid
         if pmsgid is None:
             pmsgid = self._grandparentmsgid
     kwargs = {ensure_str(k): v for (k, v) in kwargs.items()}
     msgid = log.msg(msg, facility=facility, parent=pmsgid, *args, **kwargs)
     if self._parentmsgid is None:
         self._parentmsgid = msgid
     return msgid
Esempio n. 44
0
 def _created(n):
     d = defer.succeed(None)
     d.addCallback(lambda res: n.get_servermap(MODE_READ))
     def _got_smap1(smap):
         # stash the old state of the file
         self.old_map = smap
     d.addCallback(_got_smap1)
     # then modify the file, leaving the old map untouched
     d.addCallback(lambda res: log.msg("starting winning write"))
     d.addCallback(lambda res: n.overwrite(MutableData("contents 2")))
     # now attempt to retrieve the old version with the old servermap.
     # This will look like someone has changed the file since we
     # updated the servermap.
     d.addCallback(lambda res: log.msg("starting doomed read"))
     d.addCallback(lambda res:
                   self.shouldFail(NotEnoughSharesError,
                                   "test_retrieve_surprise",
                                   "ran out of servers: have 0 of 1",
                                   n.download_version,
                                   self.old_map,
                                   self.old_map.best_recoverable_version(),
                                   ))
     return d
Esempio n. 45
0
    def __init__(self, parent, tubref, connectionPlugins):
        self._logparent = log.msg(format="TubConnector created from "
                                  "%(fromtubid)s to %(totubid)s",
                                  fromtubid=parent.tubID,
                                  totubid=tubref.getTubID(),
                                  level=OPERATIONAL,
                                  facility="foolscap.connection",
                                  umid="pH4QDA")
        self.tub = parent
        self.target = tubref
        self.connectionPlugins = connectionPlugins
        self._connectionInfo = ConnectionInfo()
        self.remainingLocations = list(self.target.getLocations())
        # attemptedLocations keeps track of where we've already tried to
        # connect, so we don't try them twice, even if they appear in the
        # hints multiple times. this isn't too clever: slight variations of
        # the same hint will fool it, but it should be enough to avoid
        # infinite redirection loops.
        self.attemptedLocations = []

        # validHints tracks which hints were successfully turned into
        # endpoints. If we don't recognize any hint type in a FURL,
        # validHints will be empty when we're done, and we'll signal
        # NoLocationHintsError
        self.validHints = []

        # pendingConnections contains a Deferred for each endpoint.connect()
        # that has started (but not yet established) a connection. We keep
        # track of these so we can shut them down (using d.cancel()) when we
        # stop connecting (either because one of the other connections
        # succeeded, or because someone told us to give up).
        self.pendingConnections = set()

        # self.pendingNegotiations maps Negotiation instances (connected but
        # not finished negotiation) to the hint that got us the connection.
        # We track these so we can abandon the negotiation.
        self.pendingNegotiations = {}
Esempio n. 46
0
 def log(self, *args, **kwargs):
     kwargs['parent'] = kwargs.get('parent') or self._logparent
     kwargs['facility'] = kwargs.get('facility') or "foolscap.connection"
     return log.msg(*args, **kwargs)
Esempio n. 47
0
 def log(self, *args, **kwargs):
     kwargs["parent"] = kwargs.get("parent") or self._logparent
     kwargs["facility"] = kwargs.get("facility") or "foolscap.connection"
     return log.msg(*args, **kwargs)
Esempio n. 48
0
    def receiveChild(self, token, ready_deferred=None):
        if self.debug:
            log.msg("%s.receiveChild: %s %s %s %s %s args=%s kwargs=%s" %
                    (self, self.closed, self.num_unreferenceable_children,
                     len(self._ready_deferreds), token, ready_deferred,
                     self.args, self.kwargs))
        if self.numargs is None:
            # this token is the number of positional arguments
            assert isinstance(token, int)
            assert ready_deferred is None
            self.numargs = token
            if self.numargs:
                ms = self.methodSchema
                if ms:
                    accept, self.argConstraint = \
                            ms.getPositionalArgConstraint(0)
                    assert accept
            return

        if len(self.args) < self.numargs:
            # this token is a positional argument
            argvalue = token
            argpos = len(self.args)
            self.args.append(argvalue)
            if isinstance(argvalue, defer.Deferred):
                # this may occur if the child is a gift which has not
                # resolved yet.
                self.num_unreferenceable_children += 1
                argvalue.addCallback(self.updateChild, argpos)
            if ready_deferred:
                if self.debug:
                    log.msg("%s.receiveChild got an unready posarg" % self)
                self._ready_deferreds.append(ready_deferred)
            if len(self.args) < self.numargs:
                # more to come
                ms = self.methodSchema
                if ms:
                    nextargnum = len(self.args)
                    accept, self.argConstraint = \
                            ms.getPositionalArgConstraint(nextargnum)
                    assert accept
            return

        if self.argname is None:
            # this token is the name of a keyword argument
            assert ready_deferred is None
            self.argname = token
            # if the argname is invalid, this may raise Violation
            ms = self.methodSchema
            if ms:
                accept, self.argConstraint = \
                        ms.getKeywordArgConstraint(self.argname,
                                                   self.numargs,
                                                   self.kwargs.keys())
                assert accept
            return

        # this token is the value of a keyword argument
        argvalue = token
        self.kwargs[self.argname] = argvalue
        if isinstance(argvalue, defer.Deferred):
            self.num_unreferenceable_children += 1
            argvalue.addCallback(self.updateChild, self.argname)
        if ready_deferred:
            if self.debug:
                log.msg("%s.receiveChild got an unready kwarg" % self)
            self._ready_deferreds.append(ready_deferred)
        self.argname = None
        return
Esempio n. 49
0
    def receiveChild(self, token, ready_deferred=None):
        assert not isinstance(token, defer.Deferred)
        if self.debug:
            log.msg("%s.receiveChild [s%d]: %s" %
                    (self, self.stage, repr(token)))

        if self.stage == 0: # reqID
            # we don't yet know which reqID to send any failure to
            assert ready_deferred is None
            self.reqID = token
            self.stage = 1
            if self.reqID != 0:
                assert self.reqID not in self.broker.activeLocalCalls
                self.broker.activeLocalCalls[self.reqID] = self
            return

        if self.stage == 1: # objID
            # this might raise an exception if objID is invalid
            assert ready_deferred is None
            self.objID = token
            try:
                self.obj = self.broker.getMyReferenceByCLID(token)
            except KeyError:
                raise Violation("unknown CLID %d" % (token,))
            #iface = self.broker.getRemoteInterfaceByName(token)
            if self.objID < 0:
                self.interface = None
            else:
                self.interface = self.obj.getInterface()
            self.stage = 2
            return

        if self.stage == 2: # methodname
            # validate the methodname, get the schema. This may raise an
            # exception for unknown methods

            # must find the schema, using the interfaces

            # TODO: getSchema should probably be in an adapter instead of in
            # a pb.Referenceable base class. Old-style (unconstrained)
            # flavors.Referenceable should be adapted to something which
            # always returns None

            # TODO: make this faster. A likely optimization is to take a
            # tuple of components.getInterfaces(obj) and use it as a cache
            # key. It would be even faster to use obj.__class__, but that
            # would probably violate the expectation that instances can
            # define their own __implements__ (independently from their
            # class). If this expectation were to go away, a quick
            # obj.__class__ -> RemoteReferenceSchema cache could be built.

            assert ready_deferred is None
            self.stage = 3

            if self.objID < 0:
                # the target is a bound method, ignore the methodname
                self.methodSchema = getattr(self.obj, "methodSchema", None)
                self.methodname = None # TODO: give it something useful
                if self.broker.requireSchema and not self.methodSchema:
                    why = "This broker does not accept unconstrained " + \
                          "method calls"
                    raise Violation(why)
                return

            self.methodname = token

            if self.interface:
                # they are calling an interface+method pair
                ms = self.interface.get(self.methodname)
                if not ms:
                    why = "method '%s' not defined in %s" % \
                          (self.methodname, self.interface.__remote_name__)
                    raise Violation(why)
                self.methodSchema = ms

            return

        if self.stage == 3: # arguments
            assert isinstance(token, ArgumentUnslicer)
            self.allargs = token
            # queue the message. It will not be executed until all the
            # arguments are ready. The .args list and .kwargs dict may change
            # before then.
            if ready_deferred:
                self._ready_deferreds.append(ready_deferred)
            self.stage = 4
            return
Esempio n. 50
0
    def fail(self, why):
        if self.active:
            if self.broker:
                self.broker.removeRequest(self)
            self.active = False
            self.failure = why
            if (self.broker and
                self.broker.tub and
                self.broker.tub.logRemoteFailures):

                my_short_tubid = "??"
                if self.broker.tub: # for tests
                    my_short_tubid = self.broker.tub.getShortTubID()
                their_short_tubid = self.broker.remote_tubref.getShortTubID()

                lp = log.msg("an outbound callRemote (that we [%s] sent to "
                             "someone else [%s]) failed on the far end"
                             % (my_short_tubid, their_short_tubid),
                             level=log.UNUSUAL)
                methname = ".".join([self.interfaceName or "?",
                                     self.methodName or "?"])
                log.msg(" reqID=%d, rref=%s, methname=%s"
                        % (self.reqID, self.rref, methname),
                        level=log.NOISY, parent=lp)
                #stack = why.getTraceback()
                # TODO: include the first few letters of the remote tubID in
                # this REMOTE tag
                #stack = "REMOTE: " + stack.replace("\n", "\nREMOTE: ")
                log.msg(" the REMOTE failure was:", failure=why,
                        level=log.NOISY, parent=lp)
                #log.msg(stack, level=log.NOISY, parent=lp)
            self.deferred.errback(why)
        else:
            log.msg("WEIRD: fail() on an inactive request", traceback=True)
            if self.failure:
                log.msg("multiple failures")
                log.msg("first one was:", self.failure)
                log.msg("this one was:", why)
                log.err("multiple failures indicate a problem")
Esempio n. 51
0
 def log(self, *args, **kwargs):
     return log.msg(*args, **kwargs)
Esempio n. 52
0
    def get_introducer_configuration(self):
        """
        Get configuration for introducers.

        :return {unicode: (unicode, FilePath)}: A mapping from introducer
            petname to a tuple of the introducer's fURL and local cache path.
        """
        introducers_yaml_filename = self.get_private_path("introducers.yaml")
        introducers_filepath = FilePath(introducers_yaml_filename)

        def get_cache_filepath(petname):
            return FilePath(
                self.get_private_path(
                    "introducer_{}_cache.yaml".format(petname)), )

        try:
            with introducers_filepath.open() as f:
                introducers_yaml = safe_load(f)
                if introducers_yaml is None:
                    raise EnvironmentError(
                        EPERM,
                        "Can't read '{}'".format(introducers_yaml_filename),
                        introducers_yaml_filename,
                    )
                introducers = {
                    petname: config["furl"]
                    for petname, config in introducers_yaml.get(
                        "introducers", {}).items()
                }
                non_strs = list(k for k in introducers.keys()
                                if not isinstance(k, str))
                if non_strs:
                    raise TypeError(
                        "Introducer petnames {!r} should have been str".format(
                            non_strs, ), )
                non_strs = list(v for v in introducers.values()
                                if not isinstance(v, str))
                if non_strs:
                    raise TypeError(
                        "Introducer fURLs {!r} should have been str".format(
                            non_strs, ), )
                log.msg("found {} introducers in {!r}".format(
                    len(introducers),
                    introducers_yaml_filename,
                ))
        except EnvironmentError as e:
            if e.errno != ENOENT:
                raise
            introducers = {}

        # supported the deprecated [client]introducer.furl item in tahoe.cfg
        tahoe_cfg_introducer_furl = self.get_config("client",
                                                    "introducer.furl", None)
        if tahoe_cfg_introducer_furl == "None":
            raise ValueError("tahoe.cfg has invalid 'introducer.furl = None':"
                             " to disable it omit the key entirely")
        if tahoe_cfg_introducer_furl:
            warn(
                "tahoe.cfg [client]introducer.furl is deprecated; "
                "use private/introducers.yaml instead.",
                category=DeprecationWarning,
                stacklevel=-1,
            )
            if "default" in introducers:
                raise ValueError(
                    "'default' introducer furl cannot be specified in tahoe.cfg and introducers.yaml;"
                    " please fix impossible configuration.")
            introducers['default'] = tahoe_cfg_introducer_furl

        return {
            petname: (furl, get_cache_filepath(petname))
            for (petname, furl) in introducers.items()
        }
Esempio n. 53
0
class Broker(banana.Banana, referenceable.Referenceable):
    """I manage a connection to a remote Broker.

    @ivar tub: the L{Tub} which contains us
    @ivar yourReferenceByCLID: maps your CLID to a RemoteReferenceData
    #@ivar yourReferenceByName: maps a per-Tub name to a RemoteReferenceData
    @ivar yourReferenceByURL: maps a global URL to a RemoteReferenceData

    """

    implements(RIBroker, IBroker)
    slicerClass = PBRootSlicer
    unslicerClass = PBRootUnslicer
    unsafeTracebacks = True
    requireSchema = False
    disconnected = False
    factory = None
    tub = None
    remote_broker = None
    startingTLS = False
    startedTLS = False
    use_remote_broker = True

    def __init__(self,
                 remote_tubref,
                 params={},
                 keepaliveTimeout=None,
                 disconnectTimeout=None,
                 connectionInfo=None):
        banana.Banana.__init__(self, params)
        self._expose_remote_exception_types = True
        self.remote_tubref = remote_tubref
        self.keepaliveTimeout = keepaliveTimeout
        self.disconnectTimeout = disconnectTimeout
        self._banana_decision_version = params.get("banana-decision-version")
        vocab_table_index = params.get('initial-vocab-table-index')
        if vocab_table_index:
            table = vocab.INITIAL_VOCAB_TABLES[vocab_table_index]
            self.populateVocabTable(table)
        self.initBroker()
        self.current_slave_IR = params.get('current-slave-IR')
        self.current_seqnum = params.get('current-seqnum')
        self.creation_timestamp = time.time()
        self._connectionInfo = connectionInfo

    def initBroker(self):

        # tracking Referenceables
        # sending side uses these
        self.nextCLID = count(1).next  # 0 is for the broker
        self.myReferenceByPUID = {}  # maps ref.processUniqueID to a tracker
        self.myReferenceByCLID = {}  # maps CLID to a tracker
        # receiving side uses these
        self.yourReferenceByCLID = {}
        self.yourReferenceByURL = {}

        # tracking Gifts
        self.nextGiftID = count(1).next
        self.myGifts = {}  # maps (broker,clid) to (rref, giftID, count)
        self.myGiftsByGiftID = {}  # maps giftID to (broker,clid)

        # remote calls
        # sending side uses these
        self.nextReqID = count(1).next  # 0 means "we don't want a response"
        self.waitingForAnswers = {}  # we wait for the other side to answer
        self.disconnectWatchers = []
        # receiving side uses these
        self.inboundDeliveryQueue = []
        self._waiting_for_call_to_be_ready = False
        self.activeLocalCalls = {}  # the other side wants an answer from us

    def setTub(self, tub):
        assert ipb.ITub.providedBy(tub)
        self.tub = tub
        self.unsafeTracebacks = tub.unsafeTracebacks
        self._expose_remote_exception_types = tub._expose_remote_exception_types
        if tub.debugBanana:
            self.debugSend = True
            self.debugReceive = True

    def connectionMade(self):
        banana.Banana.connectionMade(self)
        self.rootSlicer.broker = self
        self.rootUnslicer.broker = self
        if self.use_remote_broker:
            self._create_remote_broker()

    def _create_remote_broker(self):
        # create the remote_broker object. We don't use the usual
        # reference-counting mechanism here, because this is a synthetic
        # object that lives forever.
        tracker = referenceable.RemoteReferenceTracker(self, 0, None,
                                                       "RIBroker")
        self.remote_broker = referenceable.RemoteReference(tracker)

    # connectionTimedOut is called in response to the Banana layer detecting
    # the lack of connection activity

    def connectionTimedOut(self):
        err = error.ConnectionLost("banana timeout: connection dropped")
        why = failure.Failure(err)
        self.shutdown(why)

    def shutdown(self, why, fireDisconnectWatchers=True):
        """Stop using this connection. If fireDisconnectWatchers is False,
        all disconnect watchers are removed before shutdown, so they will not
        be called (this is appropriate when the Broker is shutting down
        because the whole Tub is being shut down). We terminate the
        connection quickly, rather than waiting for the transmit queue to
        drain.
        """
        assert isinstance(why, failure.Failure)
        if not fireDisconnectWatchers:
            self.disconnectWatchers = []
        self.finish(why)
        # loseConnection eventually provokes connectionLost()
        self.transport.loseConnection()

    def connectionLost(self, why):
        tubid = "?"
        if self.remote_tubref:
            tubid = self.remote_tubref.getShortTubID()
        log.msg("connection to %s lost" % tubid,
                facility="foolscap.connection")
        banana.Banana.connectionLost(self, why)
        self.finish(why)

    def finish(self, why):
        if self.disconnected:
            return
        assert isinstance(why, failure.Failure), why
        self.disconnected = True
        self.remote_broker = None
        self.abandonAllRequests(why)
        # TODO: why reset all the tables to something useable? There may be
        # outstanding RemoteReferences that point to us, but I don't see why
        # that requires all these empty dictionaries.
        self.myReferenceByPUID = {}
        self.myReferenceByCLID = {}
        self.yourReferenceByCLID = {}
        self.yourReferenceByURL = {}
        self.myGifts = {}
        self.myGiftsByGiftID = {}
        for (cb, args, kwargs) in self.disconnectWatchers:
            eventually(cb, *args, **kwargs)
        self.disconnectWatchers = []
        if self.tub:
            # TODO: remove the conditional. It is only here to accomodate
            # some tests: test_pb.TestCall.testDisconnect[123]
            self.tub.brokerDetached(self, why)

    def notifyOnDisconnect(self, callback, *args, **kwargs):
        marker = (callback, args, kwargs)
        if self.disconnected:
            eventually(callback, *args, **kwargs)
        else:
            self.disconnectWatchers.append(marker)
        return marker

    def dontNotifyOnDisconnect(self, marker):
        if self.disconnected:
            return
        # be tolerant of attempts to unregister a callback that has already
        # fired. I think it is hard to write safe code without this
        # tolerance.

        # TODO: on the other hand, I'm not sure this is the best policy,
        # since you lose the feedback that tells you about
        # unregistering-the-wrong-thing bugs. We need to look at the way that
        # register/unregister gets used and see if there is a way to retain
        # the typechecking that results from insisting that you can only
        # remove something that was stil in the list.
        if marker in self.disconnectWatchers:
            self.disconnectWatchers.remove(marker)

    def getConnectionInfo(self):
        return self._connectionInfo

    # methods to send my Referenceables to the other side

    def getTrackerForMyReference(self, puid, obj):
        tracker = self.myReferenceByPUID.get(puid)
        if not tracker:
            # need to add one
            clid = self.nextCLID()
            tracker = referenceable.ReferenceableTracker(
                self.tub, obj, puid, clid)
            self.myReferenceByPUID[puid] = tracker
            self.myReferenceByCLID[clid] = tracker
        return tracker

    def getTrackerForMyCall(self, puid, obj):
        # just like getTrackerForMyReference, but with a negative clid
        tracker = self.myReferenceByPUID.get(puid)
        if not tracker:
            # need to add one
            clid = self.nextCLID()
            clid = -clid
            tracker = referenceable.ReferenceableTracker(
                self.tub, obj, puid, clid)
            self.myReferenceByPUID[puid] = tracker
            self.myReferenceByCLID[clid] = tracker
        return tracker

    # methods to handle inbound 'my-reference' sequences

    def getTrackerForYourReference(self, clid, interfaceName=None, url=None):
        """The far end holds a Referenceable and has just sent us a reference
        to it (expressed as a small integer). If this is a new reference,
        they will give us an interface name too, and possibly a global URL
        for it. Obtain a RemoteReference object (creating it if necessary) to
        give to the local recipient.

        The sender remembers that we hold a reference to their object. When
        our RemoteReference goes away, we send a decref message to them, so
        they can possibly free their object. """

        assert type(interfaceName) is str or interfaceName is None
        if url is not None:
            assert type(url) is str
        tracker = self.yourReferenceByCLID.get(clid)
        if not tracker:
            # TODO: translate interfaceNames to RemoteInterfaces
            if clid >= 0:
                trackerclass = referenceable.RemoteReferenceTracker
            else:
                trackerclass = referenceable.RemoteMethodReferenceTracker
            tracker = trackerclass(self, clid, url, interfaceName)
            self.yourReferenceByCLID[clid] = tracker
            if url:
                self.yourReferenceByURL[url] = tracker
        return tracker

    def freeYourReference(self, tracker, count):
        # this is called when the RemoteReference is deleted
        if not self.remote_broker:  # tests do not set this up
            self.freeYourReferenceTracker(None, tracker)
            return
        try:
            rb = self.remote_broker
            # TODO: do we want callRemoteOnly here? is there a way we can
            # avoid wanting to know when the decref has completed? Only if we
            # send the interface list and URL on every occurrence of the
            # my-reference sequence. Either A) we use callRemote("decref")
            # and wait until the ack to free the tracker, or B) we use
            # callRemoteOnly("decref") and free the tracker right away. In
            # case B, the far end has no way to know that we've just freed
            # the tracker and will therefore forget about everything they
            # told us (including the interface list), so they cannot
            # accurately do anything special on the "first" send of this
            # reference. Which means that if we do B, we must either send
            # that extra information on every my-reference sequence, or do
            # without it, or make it optional, or retrieve it separately, or
            # something.

            # rb.callRemoteOnly("decref", clid=tracker.clid, count=count)
            # self.freeYourReferenceTracker('bogus', tracker)
            # return

            d = rb.callRemote("decref", clid=tracker.clid, count=count)

            # if the connection was lost before we can get an ack, we're
            # tearing this down anyway
            def _ignore_loss(f):
                f.trap(DeadReferenceError, *LOST_CONNECTION_ERRORS)
                return None

            d.addErrback(_ignore_loss)
            # once the ack comes back, or if we know we'll never get one,
            # release the tracker
            d.addCallback(self.freeYourReferenceTracker, tracker)
        except:
            f = failure.Failure()
            log.msg("failure during freeRemoteReference",
                    facility="foolscap",
                    level=log.UNUSUAL,
                    failure=f)

    def freeYourReferenceTracker(self, res, tracker):
        if tracker.received_count != 0:
            return
        if self.yourReferenceByCLID.has_key(tracker.clid):
            del self.yourReferenceByCLID[tracker.clid]
        if tracker.url and self.yourReferenceByURL.has_key(tracker.url):
            del self.yourReferenceByURL[tracker.url]

    # methods to handle inbound 'your-reference' sequences

    def getMyReferenceByCLID(self, clid):
        """clid is the connection-local ID of the Referenceable the other
        end is trying to invoke or point to. If it is a number, they want an
        implicitly-created per-connection object that we sent to them at
        some point in the past. If it is a string, they want an object that
        was registered with our Factory.
        """

        assert isinstance(clid, (int, long))
        if clid == 0:
            return self
        return self.myReferenceByCLID[clid].obj
        # obj = IReferenceable(obj)
        # assert isinstance(obj, pb.Referenceable)
        # obj needs .getMethodSchema, which needs .getArgConstraint

    def remote_decref(self, clid, count):
        # invoked when the other side sends us a decref message
        assert isinstance(clid, (int, long))
        assert clid != 0
        tracker = self.myReferenceByCLID.get(clid, None)
        if not tracker:
            return  # already gone, probably because we're shutting down
        done = tracker.decref(count)
        if done:
            del self.myReferenceByPUID[tracker.puid]
            del self.myReferenceByCLID[clid]

    # methods to send RemoteReference 'gifts' to third-parties

    def makeGift(self, rref):
        # return the giftid
        broker, clid = rref.tracker.broker, rref.tracker.clid
        i = (broker, clid)
        old = self.myGifts.get(i)
        if old:
            rref, giftID, count = old
            self.myGifts[i] = (rref, giftID, count + 1)
        else:
            giftID = self.nextGiftID()
            self.myGiftsByGiftID[giftID] = i
            self.myGifts[i] = (rref, giftID, 1)
        return giftID

    def remote_decgift(self, giftID, count):
        broker, clid = self.myGiftsByGiftID[giftID]
        rref, giftID, gift_count = self.myGifts[(broker, clid)]
        gift_count -= count
        if gift_count == 0:
            del self.myGiftsByGiftID[giftID]
            del self.myGifts[(broker, clid)]
        else:
            self.myGifts[(broker, clid)] = (rref, giftID, gift_count)

    # methods to deal with URLs

    def getYourReferenceByName(self, name):
        d = self.remote_broker.callRemote("getReferenceByName", name=name)
        return d

    def remote_getReferenceByName(self, name):
        return self.tub.getReferenceForName(name)

    # remote-method-invocation methods, calling side, invoked by
    # RemoteReference.callRemote and CallSlicer

    def newRequestID(self):
        if self.disconnected:
            raise DeadReferenceError("Calling Stale Broker")
        return self.nextReqID()

    def addRequest(self, req):
        req.broker = self
        self.waitingForAnswers[req.reqID] = req

    def removeRequest(self, req):
        del self.waitingForAnswers[req.reqID]

    def getRequest(self, reqID):
        # invoked by AnswerUnslicer and ErrorUnslicer
        try:
            return self.waitingForAnswers[reqID]
        except KeyError:
            raise Violation("non-existent reqID '%d'" % reqID)

    def abandonAllRequests(self, why):
        for req in self.waitingForAnswers.values():
            if why.check(*LOST_CONNECTION_ERRORS):
                # map all connection-lost errors to DeadReferenceError, so
                # application code only needs to check for one exception type
                tubid = None
                # since we're creating a new exception object for each call,
                # let's add more information to it
                if self.remote_tubref:
                    tubid = self.remote_tubref.getShortTubID()
                e = DeadReferenceError("Connection was lost", tubid, req)
                why = failure.Failure(e)
            eventually(req.fail, why)

    # target-side, invoked by CallUnslicer

    def getRemoteInterfaceByName(self, riname):
        # this lives in the broker because it ought to be per-connection
        return remoteinterface.RemoteInterfaceRegistry[riname]

    def getSchemaForMethod(self, rifaces, methodname):
        # this lives in the Broker so it can override the resolution order,
        # not that overlapping RemoteInterfaces should be allowed to happen
        # all that often
        for ri in rifaces:
            m = ri.get(methodname)
            if m:
                return m
        return None

    def scheduleCall(self, delivery, ready_deferred):
        self.inboundDeliveryQueue.append((delivery, ready_deferred))
        eventually(self.doNextCall)

    def doNextCall(self):
        if self.disconnected:
            return
        if self._waiting_for_call_to_be_ready:
            return
        if not self.inboundDeliveryQueue:
            return
        delivery, ready_deferred = self.inboundDeliveryQueue.pop(0)
        self._waiting_for_call_to_be_ready = True
        if not ready_deferred:
            ready_deferred = defer.succeed(None)
        d = ready_deferred

        def _ready(res):
            self._waiting_for_call_to_be_ready = False
            eventually(self.doNextCall)
            return res

        d.addBoth(_ready)

        # at this point, the Deferred chain for this one delivery runs
        # independently of any other, and methods which take a long time to
        # complete will not hold up other methods. We must call _doCall and
        # let the remote_ method get control before we process any other
        # message, but the eventually() above insures we'll have a chance to
        # do that before we give up control.

        d.addCallback(lambda res: self._doCall(delivery))
        d.addCallback(self._callFinished, delivery)
        d.addErrback(self.callFailed, delivery.reqID, delivery)
        d.addErrback(log.err)
        return None

    def _doCall(self, delivery):
        # our ordering rules require that the order in which each
        # remote_foo() method gets control is exactly the same as the order
        # in which the original caller invoked callRemote(). To insure this,
        # _startCall() is not allowed to insert additional delays before it
        # runs doRemoteCall() on the target object.
        obj = delivery.obj
        args = delivery.allargs.args
        kwargs = delivery.allargs.kwargs
        for i in args + kwargs.values():
            assert not isinstance(i, defer.Deferred)

        if delivery.methodSchema:
            # we asked about each argument on the way in, but ask again so
            # they can look for missing arguments. TODO: see if we can remove
            # the redundant per-argument checks.
            delivery.methodSchema.checkAllArgs(args, kwargs, True)

        # interesting case: if the method completes successfully, but
        # our schema prohibits us from sending the result (perhaps the
        # method returned an int but the schema insists upon a string).
        # TODO: move the return-value schema check into
        # Referenceable.doRemoteCall, so the exception's traceback will be
        # attached to the object that caused it
        if delivery.methodname is None:
            assert callable(obj)
            return obj(*args, **kwargs)
        else:
            obj = ipb.IRemotelyCallable(obj)
            return obj.doRemoteCall(delivery.methodname, args, kwargs)

    def _callFinished(self, res, delivery):
        reqID = delivery.reqID
        if reqID == 0:
            return
        methodSchema = delivery.methodSchema
        assert self.activeLocalCalls[reqID]
        methodName = None
        if methodSchema:
            methodName = methodSchema.name
            try:
                methodSchema.checkResults(res, False)  # may raise Violation
            except Violation, v:
                v.prependLocation("in return value of %s.%s" %
                                  (delivery.obj, methodSchema.name))
                raise

        answer = call.AnswerSlicer(reqID, res, methodName)
        # once the answer has started transmitting, any exceptions must be
        # logged and dropped, and not turned into an Error to be sent.
        try:
            self.send(answer)
            # TODO: .send should return a Deferred that fires when the last
            # byte has been queued, and we should delete the local note then
        except:
            f = failure.Failure()
            log.msg("Broker._callfinished unable to send",
                    facility="foolscap",
                    level=log.UNUSUAL,
                    failure=f)
        del self.activeLocalCalls[reqID]
Esempio n. 54
0
 def stopFactory(self):
     log.msg("Stopping factory %r" % self, facility="foolscap.listener")
     return protocol.ServerFactory.stopFactory(self)
Esempio n. 55
0
 def log(self, *args, **kwargs):
     kwargs['tubID'] = self.tubID
     return log.msg(*args, **kwargs)
Esempio n. 56
0
 def log(self, *args, **kwargs):
     return log.msg(*args, **kwargs)
Esempio n. 57
0
 def log(self, *args, **kwargs):
     kwargs['tubID'] = self.tubID
     return log.msg(*args, **kwargs)