Changeset 209:87a1dce6b4ce
- Timestamp:
- 07/26/07 16:40:56 (3 years ago)
- Files:
-
- ChangeLog (modified) (1 diff)
- foolscap/broker.py (modified) (3 diffs)
- foolscap/call.py (modified) (18 diffs)
- foolscap/referenceable.py (modified) (2 diffs)
- foolscap/slicer.py (modified) (3 diffs)
- foolscap/slicers/dict.py (modified) (2 diffs)
- foolscap/slicers/list.py (modified) (2 diffs)
- foolscap/slicers/set.py (modified) (2 diffs)
- foolscap/slicers/tuple.py (modified) (3 diffs)
- foolscap/test/test_gifts.py (modified) (16 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
ChangeLog
r208 r209 1 2007-07-26 Brian Warner <warner@lothar.com> 2 3 * foolscap/slicer.py (BaseUnslicer.receiveChild): new convention: 4 Unslicers should accumulate their children's ready_deferreds into 5 an AsyncAND, and pass it to the parent. If something goes wrong, 6 the ready_deferred should errback, which will abandon the method 7 call that contains it. 8 * foolscap/slicers/dict.py (DictUnslicer.receiveClose): same 9 * foolscap/slicers/tuple.py (TupleUnslicer.receiveClose): same 10 (TupleUnslicer.complete): same 11 * foolscap/slicers/set.py (SetUnslicer.receiveClose): same 12 * foolscap/slicers/list.py (ListUnslicer.receiveClose): same 13 * foolscap/call.py (CallUnslicer.receiveClose): same 14 15 * foolscap/referenceable.py (TheirReferenceUnslicer.receiveClose): 16 use our ready_deferred to signal whether the gift resolves 17 correctly or not. If it fails, errback ready_deferred (to prevent 18 the message from being delivered without the resolved gift), but 19 callback obj_deferred with a placeholder to avoid causing too much 20 distress to the container. 21 22 * foolscap/broker.py (PBRootUnslicer.receiveChild): accept 23 ready_deferred in the InboundDelivery, stash both of them in the 24 broker. 25 (Broker.scheduleCall): rewrite inbound delivery handling: use a 26 self._call_is_running flag to prevent concurrent deliveries, and 27 wait for the ready_deferred before delivering the top-most 28 message. If the ready_deferred errbacks, that gets routed to 29 self.callFailed so the caller hears about the problem. This closes 30 ticket #2. 31 32 * foolscap/call.py (InboundDelivery): remove whenRunnable, relying 33 upon the ready_deferred to let the Broker know when the message 34 can be delivered. 35 (ArgumentUnslicer): significant cleanup, using ready_deferred. 36 Remove isReady and whenReady. 37 38 * foolscap/test/test_gifts.py (Base): factor setup code out 39 (Base.createCharacters): registerReference(tubname), for debugging 40 (Bad): add a bunch of tests to make sure that gifts which fail to 41 resolve (for various reasons) will inform the caller about the 42 problem, via an errback on the original callRemote()'s Deferred. 43 1 44 2007-07-25 Brian Warner <warner@lothar.com> 2 45 foolscap/broker.py
r170 r209 112 112 def receiveChild(self, token, ready_deferred): 113 113 if isinstance(token, call.InboundDelivery): 114 assert ready_deferred is None 115 self.broker.scheduleCall(token) 114 self.broker.scheduleCall(token, ready_deferred) 116 115 117 116 … … 216 215 # receiving side uses these 217 216 self.inboundDeliveryQueue = [] 217 self._call_is_running = False 218 218 self.activeLocalCalls = {} # the other side wants an answer from us 219 219 … … 507 507 return None 508 508 509 def scheduleCall(self, delivery ):510 self.inboundDeliveryQueue.append( delivery)509 def scheduleCall(self, delivery, ready_deferred): 510 self.inboundDeliveryQueue.append( (delivery,ready_deferred) ) 511 511 eventually(self.doNextCall) 512 512 513 def doNextCall(self, ignored=None): 513 def doNextCall(self): 514 if self._call_is_running: 515 return 514 516 if not self.inboundDeliveryQueue: 515 517 return 516 nextCall = self.inboundDeliveryQueue[0] 517 if nextCall.isRunnable(): 518 # remove it and arrange to run again soon 519 self.inboundDeliveryQueue.pop(0) 520 delivery = nextCall 521 if self.inboundDeliveryQueue: 522 eventually(self.doNextCall) 523 524 # now perform the actual delivery 525 d = defer.maybeDeferred(self._doCall, delivery) 526 d.addCallback(self._callFinished, delivery) 527 d.addErrback(self.callFailed, delivery.reqID, delivery) 528 return 529 # arrange to wake up when the next call becomes runnable 530 d = nextCall.whenRunnable() 531 d.addCallback(self.doNextCall) 518 delivery, ready_deferred = self.inboundDeliveryQueue.pop(0) 519 self._call_is_running = True 520 if not ready_deferred: 521 ready_deferred = defer.succeed(None) 522 d = ready_deferred 523 d.addCallback(lambda res: self._doCall(delivery)) 524 d.addCallback(self._callFinished, delivery) 525 d.addErrback(self.callFailed, delivery.reqID, delivery) 526 def _done(res): 527 self._call_is_running = False 528 eventually(self.doNextCall) 529 d.addBoth(_done) 530 return None 532 531 533 532 def _doCall(self, delivery): 534 533 obj = delivery.obj 535 assert delivery.allargs.isReady()536 534 args = delivery.allargs.args 537 535 kwargs = delivery.allargs.kwargs foolscap/call.py
r207 r209 4 4 5 5 from foolscap import copyable, slicer, tokens 6 from foolscap.eventual import eventually7 6 from foolscap.copyable import AttributeDictConstraint 8 7 from foolscap.constraint import ByteStringConstraint 9 8 from foolscap.slicers.list import ListConstraint 10 9 from tokens import BananaError, Violation 10 from foolscap.util import AsyncAND 11 11 12 12 … … 163 163 self.methodSchema = methodSchema 164 164 self.allargs = allargs 165 if allargs.isReady():166 self.runnable = True167 self.runnable = False168 169 def isRunnable(self):170 if self.allargs.isReady():171 return True172 return False173 174 def whenRunnable(self):175 if self.allargs.isReady():176 return defer.succeed(self)177 d = self.allargs.whenReady()178 d.addCallback(lambda res: self)179 return d180 165 181 166 def logFailure(self, f): … … 212 197 self.argConstraint = None 213 198 self.num_unreferenceable_children = 0 214 self.num_unready_children = 0 199 self._all_children_are_referenceable_d = None 200 self._ready_deferreds = [] 215 201 self.closed = False 216 202 … … 249 235 log.msg("%s.receiveChild: %s %s %s %s %s args=%s kwargs=%s" % 250 236 (self, self.closed, self.num_unreferenceable_children, 251 self.num_unready_children, token, ready_deferred,237 len(self._ready_deferreds), token, ready_deferred, 252 238 self.args, self.kwargs)) 253 239 if self.numargs is None: … … 274 260 self.num_unreferenceable_children += 1 275 261 argvalue.addCallback(self.updateChild, argpos) 276 argvalue.addErrback(self.explode)277 262 if ready_deferred: 278 263 if self.debug: 279 264 log.msg("%s.receiveChild got an unready posarg" % self) 280 self.num_unready_children += 1 281 ready_deferred.addCallback(self.childReady) 265 self._ready_deferreds.append(ready_deferred) 282 266 if len(self.args) < self.numargs: 283 267 # more to come … … 292 276 if self.argname is None: 293 277 # this token is the name of a keyword argument 278 assert ready_deferred is None 294 279 self.argname = token 295 280 # if the argname is invalid, this may raise Violation … … 309 294 self.num_unreferenceable_children += 1 310 295 argvalue.addCallback(self.updateChild, self.argname) 311 argvalue.addErrback(self.explode)312 296 if ready_deferred: 313 297 if self.debug: 314 298 log.msg("%s.receiveChild got an unready kwarg" % self) 315 self.num_unready_children += 1 316 ready_deferred.addCallback(self.childReady) 299 self._ready_deferreds.append(ready_deferred) 317 300 self.argname = None 318 301 return … … 334 317 self.kwargs[which] = obj 335 318 self.num_unreferenceable_children -= 1 336 self.checkComplete() 319 if self.num_unreferenceable_children == 0: 320 if self._all_children_are_referenceable_d: 321 self._all_children_are_referenceable_d.callback(None) 337 322 return obj 338 323 339 def childReady(self, obj):340 self.num_unready_children -= 1341 if self.debug:342 log.msg("%s.childReady, now %d left" %343 (self, self.num_unready_children))344 log.msg(" obj=%s, args=%s, kwargs=%s" %345 (obj, self.args, self.kwargs))346 self.checkComplete()347 return obj348 349 def checkComplete(self):350 # this is called each time one of our children gets updated or351 # becomes ready (like when a Gift is finally resolved)352 if self.debug:353 log.msg("%s.checkComplete: %s %s %s args=%s kwargs=%s" %354 (self, self.closed, self.num_unreferenceable_children,355 self.num_unready_children, self.args, self.kwargs))356 357 if not self.closed:358 return359 if self.num_unreferenceable_children:360 return361 if self.num_unready_children:362 return363 # yup, we're done. Notify anyone who is still waiting364 if self.debug:365 log.msg(" we are ready")366 for d in self.watchers:367 eventually(d.callback, self)368 del self.watchers369 324 370 325 def receiveClose(self): … … 372 327 log.msg("%s.receiveClose: %s %s %s" % 373 328 (self, self.closed, self.num_unreferenceable_children, 374 self.num_unready_children))329 len(self._ready_deferreds))) 375 330 if (self.numargs is None or 376 331 len(self.args) < self.numargs or … … 378 333 raise BananaError("'arguments' sequence ended too early") 379 334 self.closed = True 380 self.watchers = [] 381 # we don't return a ready_deferred. Instead, the InboundDelivery 382 # object queries our isReady() method directly. 383 return self, None 384 385 def isReady(self): 386 assert self.closed 335 dl = [] 387 336 if self.num_unreferenceable_children: 388 return False 389 if self.num_unready_children: 390 return False 391 return True 392 393 def whenReady(self): 394 assert self.closed 395 if self.isReady(): 396 return defer.succeed(self) 397 d = defer.Deferred() 398 self.watchers.append(d) 399 return d 337 d = self._all_children_are_referenceable_d = defer.Deferred() 338 dl.append(d) 339 dl.extend(self._ready_deferreds) 340 ready_deferred = None 341 if dl: 342 ready_deferred = AsyncAND(dl) 343 return self, ready_deferred 400 344 401 345 def describe(self): … … 410 354 s += " arg[?]" 411 355 if self.closed: 412 if self.isReady(): 413 # waiting to be delivered 414 s += " ready" 415 else: 416 s += " waiting" 356 s += " closed" 357 # TODO: it would be nice to indicate if we still have unready 358 # children 417 359 s += ">" 418 360 return s … … 431 373 self.methodname = None 432 374 self.methodSchema = None # will be a MethodArgumentsConstraint 375 self._ready_deferreds = [] 433 376 434 377 def checkToken(self, typebyte, size): … … 473 416 def receiveChild(self, token, ready_deferred=None): 474 417 assert not isinstance(token, defer.Deferred) 475 assert ready_deferred is None476 418 if self.debug: 477 419 log.msg("%s.receiveChild [s%d]: %s" % … … 480 422 if self.stage == 0: # reqID 481 423 # we don't yet know which reqID to send any failure to 424 assert ready_deferred is None 482 425 self.reqID = token 483 426 self.stage = 1 … … 489 432 if self.stage == 1: # objID 490 433 # this might raise an exception if objID is invalid 434 assert ready_deferred is None 491 435 self.objID = token 492 436 self.obj = self.broker.getMyReferenceByCLID(token) … … 518 462 # obj.__class__ -> RemoteReferenceSchema cache could be built. 519 463 464 assert ready_deferred is None 520 465 self.stage = 3 521 466 … … 549 494 # arguments are ready. The .args list and .kwargs dict may change 550 495 # before then. 496 if ready_deferred: 497 self._ready_deferreds.append(ready_deferred) 551 498 self.stage = 4 552 499 return … … 560 507 self.methodSchema, 561 508 self.allargs) 562 return delivery, None 509 ready_deferred = None 510 if self._ready_deferreds: 511 ready_deferred = AsyncAND(self._ready_deferreds) 512 return delivery, ready_deferred 563 513 564 514 def describe(self): foolscap/referenceable.py
r191 r209 12 12 Interface = interface.Interface 13 13 from twisted.internet import defer 14 from twisted.python import failure 14 from twisted.python import failure, log 15 15 16 16 from foolscap import ipb, slicer, tokens, call … … 636 636 # complete. See to it that we fire the object deferred before we fire 637 637 # the ready_deferred. 638 obj_deferred, ready_deferred = defer.Deferred(), defer.Deferred() 638 639 obj_deferred = defer.Deferred() 640 ready_deferred = defer.Deferred() 641 639 642 def _ready(rref): 640 643 obj_deferred.callback(rref) 641 644 ready_deferred.callback(rref) 642 d.addCallback(_ready) 645 def _failed(f): 646 # if an error in getReference() occurs, log it locally (with 647 # priority UNUSUAL), because this end might need to diagnose some 648 # connection or networking problems. 649 log.msg("gift (%s) failed to resolve: %s" % (self.url, f)) 650 # deliver a placeholder object to the container, but signal the 651 # ready_deferred that we've failed. This will bubble up to the 652 # enclosing InboundDelivery, and when it gets to the top of the 653 # queue, it will be flunked. 654 obj_deferred.callback("Place holder for a Gift which failed to " 655 "resolve: %s" % f) 656 ready_deferred.errback(f) 657 d.addCallbacks(_ready, _failed) 643 658 644 659 return obj_deferred, ready_deferred foolscap/slicer.py
r88 r209 2 2 3 3 from twisted.python.components import registerAdapter 4 from twisted.python import log 4 5 from zope.interface import implements 5 6 from twisted.internet.defer import Deferred … … 191 192 192 193 def receiveChild(self, obj, ready_deferred=None): 194 """Unslicers for containers should accumulate their children's 195 ready_deferreds, then combine them in an AsyncAND when receiveClose() 196 happens, and return the AsyncAND as the ready_deferreds half of the 197 receiveClose() return value. 198 """ 193 199 pass 194 200 … … 222 228 223 229 def explode(self, failure): 224 """If something goes wrong in a Deferred callback, it may be too 225 late to reject the token and to normal error handling. I haven't 226 figured out how to do sensible error-handling in this situation. 227 This method exists to make sure that the exception shows up 228 *somewhere*. If this is called, it is also likely that a placeholder 229 (probably a Deferred) will be left in the unserialized object about 230 to be handed to the RootUnslicer. 231 """ 232 print "KABOOM" 233 print failure 230 """If something goes wrong in a Deferred callback, it may be too late 231 to reject the token and to normal error handling. I haven't figured 232 out how to do sensible error-handling in this situation. This method 233 exists to make sure that the exception shows up *somewhere*. If this 234 is called, it is also likely that a placeholder (probably a Deferred) 235 will be left in the unserialized object graph about to be handed to 236 the RootUnslicer. 237 """ 238 239 # RootUnslicer pays attention to this .exploded attribute and refuses 240 # to deliver anything if it is set. But PBRootUnslicer ignores it. 241 # TODO: clean this up, and write some unit tests to trigger it (by 242 # violating schemas?) 243 log.msg("BaseUnslicer.explode: %s" % failure) 234 244 self.protocol.exploded = failure 235 245 foolscap/slicers/dict.py
r187 r209 2 2 3 3 from twisted.python import log 4 from twisted.internet.defer import Deferred , DeferredList4 from twisted.internet.defer import Deferred 5 5 from foolscap.tokens import Violation, BananaError 6 6 from foolscap.slicer import BaseSlicer, BaseUnslicer 7 7 from foolscap.constraint import OpenerConstraint, Any, UnboundedSchema, IConstraint 8 from foolscap.util import AsyncAND 8 9 9 10 class DictSlicer(BaseSlicer): … … 106 107 ready_deferred = None 107 108 if self._ready_deferreds: 108 ready_deferred = DeferredList(self._ready_deferreds)109 ready_deferred = AsyncAND(self._ready_deferreds) 109 110 return self.d, ready_deferred 110 111 foolscap/slicers/list.py
r187 r209 2 2 3 3 from twisted.python import log 4 from twisted.internet.defer import Deferred , DeferredList4 from twisted.internet.defer import Deferred 5 5 from foolscap.tokens import Violation 6 6 from foolscap.slicer import BaseSlicer, BaseUnslicer 7 7 from foolscap.constraint import OpenerConstraint, Any, UnboundedSchema, IConstraint 8 from foolscap.util import AsyncAND 8 9 9 10 … … 106 107 ready_deferred = None 107 108 if self._ready_deferreds: 108 ready_deferred = DeferredList(self._ready_deferreds)109 ready_deferred = AsyncAND(self._ready_deferreds) 109 110 return self.list, ready_deferred 110 111 foolscap/slicers/set.py
r187 r209 10 10 from foolscap.constraint import OpenerConstraint, UnboundedSchema, Any, \ 11 11 IConstraint 12 from foolscap.util import AsyncAND 12 13 13 14 class SetSlicer(ListSlicer): … … 137 138 ready_deferred = None 138 139 if self._ready_deferreds: 139 ready_deferred = defer.DeferredList(self._ready_deferreds)140 ready_deferred = AsyncAND(self._ready_deferreds) 140 141 return self.set, ready_deferred 141 142 foolscap/slicers/tuple.py
r187 r209 1 1 # -*- test-case-name: foolscap.test.test_banana -*- 2 2 3 from twisted.internet.defer import Deferred , DeferredList3 from twisted.internet.defer import Deferred 4 4 from foolscap.tokens import Violation 5 5 from foolscap.slicer import BaseUnslicer 6 6 from foolscap.slicers.list import ListSlicer 7 7 from foolscap.constraint import OpenerConstraint, Any, UnboundedSchema, IConstraint 8 from foolscap.util import AsyncAND 8 9 9 10 … … 92 93 ready_deferred = None 93 94 if self._ready_deferreds: 94 ready_deferred = DeferredList(self._ready_deferreds)95 ready_deferred = AsyncAND(self._ready_deferreds) 95 96 96 97 t = tuple(self.list) … … 112 113 ready_deferred = None 113 114 if self._ready_deferreds: 114 ready_deferred = DeferredList(self._ready_deferreds)115 ready_deferred = AsyncAND(self._ready_deferreds) 115 116 return self.deferred, ready_deferred 116 117 foolscap/test/test_gifts.py
r189 r209 2 2 from zope.interface import implements 3 3 from twisted.trial import unittest 4 from twisted.internet import defer 5 from twisted.internet.error import ConnectionDone, ConnectionLost 4 from twisted.internet import defer, protocol, reactor 5 from twisted.internet.error import ConnectionDone, ConnectionLost, \ 6 ConnectionRefusedError 7 from twisted.python import failure 6 8 from foolscap import Tub, UnauthenticatedTub, RemoteInterface, Referenceable 7 from foolscap.referenceable import RemoteReference 9 from foolscap.referenceable import RemoteReference, SturdyRef 8 10 from foolscap.test.common import HelperTarget, RIHelper 9 11 from foolscap.eventual import flushEventualQueue 12 from foolscap.tokens import BananaError, NegotiationError 10 13 11 14 crypto_available = False … … 39 42 self.obj = obj 40 43 41 class Gifts(unittest.TestCase): 42 # Here we test the three-party introduction process as depicted in the 43 # classic Granovetter diagram. Alice has a reference to Bob and another 44 # one to Carol. Alice wants to give her Carol-reference to Bob, by 45 # including it as the argument to a method she invokes on her 46 # Bob-reference. 44 class Base: 47 45 48 46 debug = False 49 47 50 48 def setUp(self): 51 self.services = [GoodEnoughTub() , GoodEnoughTub(), GoodEnoughTub()]52 self.tubA, self.tubB, self.tubC = self.services49 self.services = [GoodEnoughTub() for i in range(4)] 50 self.tubA, self.tubB, self.tubC, self.tubD = self.services 53 51 for s in self.services: 54 52 s.startService() … … 64 62 self.alice = HelperTarget("alice") 65 63 self.bob = HelperTarget("bob") 66 self.bob_url = self.tubB.registerReference(self.bob )64 self.bob_url = self.tubB.registerReference(self.bob, "bob") 67 65 self.carol = HelperTarget("carol") 68 self.carol_url = self.tubC.registerReference(self.carol )66 self.carol_url = self.tubC.registerReference(self.carol, "carol") 69 67 # cindy is Carol's little sister. She doesn't have a phone, but 70 68 # Carol might talk about her anyway. … … 76 74 self.colette = HelperTarget("colette") 77 75 self.courtney = HelperTarget("courtney") 76 self.dave = HelperTarget("dave") 77 self.dave_url = self.tubD.registerReference(self.dave, "dave") 78 78 79 79 def createInitialReferences(self): … … 91 91 if self.debug: print "Alice got carol" 92 92 self.acarol = acarol # Alice's reference to Carol 93 d = self.tubB.getReference(self.dave_url) 94 return d 93 95 d.addCallback(_aliceGotCarol) 96 def _bobGotDave(bdave): 97 self.bdave = bdave 98 d.addCallback(_bobGotDave) 94 99 return d 95 100 … … 98 103 dl = [] 99 104 100 url = self.tubC.registerReference(self.charlene )105 url = self.tubC.registerReference(self.charlene, "charlene") 101 106 d = self.tubA.getReference(url) 102 107 def _got_charlene(rref): … … 105 110 dl.append(d) 106 111 107 url = self.tubC.registerReference(self.christine )112 url = self.tubC.registerReference(self.christine, "christine") 108 113 d = self.tubA.getReference(url) 109 114 def _got_christine(rref): … … 112 117 dl.append(d) 113 118 114 url = self.tubC.registerReference(self.clarisse )119 url = self.tubC.registerReference(self.clarisse, "clarisse") 115 120 d = self.tubA.getReference(url) 116 121 def _got_clarisse(rref): … … 119 124 dl.append(d) 120 125 121 url = self.tubC.registerReference(self.colette )126 url = self.tubC.registerReference(self.colette, "colette") 122 127 d = self.tubA.getReference(url) 123 128 def _got_colette(rref): … … 126 131 dl.append(d) 127 132 128 url = self.tubC.registerReference(self.courtney )133 url = self.tubC.registerReference(self.courtney, "courtney") 129 134 d = self.tubA.getReference(url) 130 135 def _got_courtney(rref): … … 132 137 d.addCallback(_got_courtney) 133 138 dl.append(d) 139 134 140 return defer.DeferredList(dl) 141 142 def shouldFail(self, res, expected_failure, which, substring=None): 143 # attach this with: 144 # d = something() 145 # d.addBoth(self.shouldFail, IndexError, "something") 146 # the 'which' string helps to identify which call to shouldFail was 147 # triggered, since certain versions of Twisted don't display this 148 # very well. 149 150 if isinstance(res, failure.Failure): 151 res.trap(expected_failure) 152 if substring: 153 self.failUnless(substring in str(res), 154 "substring '%s' not in '%s'" 155 % (substring, str(res))) 156 else: 157 self.fail("%s was supposed to raise %s, not get '%s'" % 158 (which, expected_failure, res)) 159 160 class Gifts(Base, unittest.TestCase): 161 # Here we test the three-party introduction process as depicted in the 162 # classic Granovetter diagram. Alice has a reference to Bob and another 163 # one to Carol. Alice wants to give her Carol-reference to Bob, by 164 # including it as the argument to a method she invokes on her 165 # Bob-reference. 135 166 136 167 def testGift(self): … … 164 195 d.addCallback(_carolCalled) 165 196 return d 166 167 197 168 198 def testImplicitGift(self): … … 227 257 return d 228 258 229 230 259 def testOrdering(self): 231 260 self.createCharacters() … … 304 333 self.alice = HelperTarget("alice") 305 334 self.bob = ConstrainedHelper("bob") 306 self.bob_url = self.tubB.registerReference(self.bob )335 self.bob_url = self.tubB.registerReference(self.bob, "bob") 307 336 self.carol = HelperTarget("carol") 308 self.carol_url = self.tubC.registerReference(self.carol) 337 self.carol_url = self.tubC.registerReference(self.carol, "carol") 338 self.dave = HelperTarget("dave") 339 self.dave_url = self.tubD.registerReference(self.dave, "dave") 309 340 310 341 def test_constraint(self): … … 319 350 d.addCallback(_checkBob) 320 351 return d 352 353 321 354 322 355 # this was used to alice's reference to carol (self.acarol) appeared in … … 360 393 return d 361 394 395 396 class Bad(Base, unittest.TestCase): 397 398 # if the recipient cannot claim their gift, the caller should see an 399 # errback. 400 401 def test_swissnum(self): 402 self.createCharacters() 403 d = self.createInitialReferences() 404 d.addCallback(lambda res: self.tubA.getReference(self.dave_url)) 405 def _introduce(adave): 406 # now break the gift to insure that Bob is unable to claim it. 407 # The first way to do this is to simple mangle the swissnum, 408 # which will result in a failure in remote_getReferenceByName. 409 # NOTE: this will have to change when we modify the way gifts are 410 # referenced, since tracker.url is scheduled to go away. 411 r = SturdyRef(adave.tracker.url) 412 r.name += ".MANGLED" 413 adave.tracker.url = r.getURL() 414 return self.acarol.callRemote("set", adave) 415 d.addCallback(_introduce) 416 d.addBoth(self.shouldFail, KeyError, "Bad.test_swissnum") 417 # make sure we can still talk to Carol, though 418 d.addCallback(lambda res: self.acarol.callRemote("set", 14)) 419 d.addCallback(lambda res: self.failUnlessEqual(self.carol.obj, 14)) 420 return d 421 test_swissnum.timeout = 10 422 423 def test_tubid(self): 424 self.createCharacters() 425 d = self.createInitialReferences() 426 d.addCallback(lambda res: self.tubA.getReference(self.dave_url)) 427 def _introduce(adave): 428 # The second way is to mangle the tubid, which will result in a 429 # failure during negotiation. NOTE: this will have to change when 430 # we modify the way gifts are referenced, since tracker.url is 431 # scheduled to go away. 432 r = SturdyRef(adave.tracker.url) 433 r.tubID += ".MANGLED" 434 adave.tracker.url = r.getURL() 435 return self.acarol.callRemote("set", adave) 436 d.addCallback(_introduce) 437 d.addBoth(self.shouldFail, BananaError, "Bad.test_tubid", 438 "unknown TubID") 439 return d 440 test_tubid.timeout = 10 441 442 def test_location(self): 443 self.createCharacters() 444 d = self.createInitialReferences() 445 d.addCallback(lambda res: self.tubA.getReference(self.dave_url)) 446 def _introduce(adave): 447 # The third way is to mangle the location hints, which will 448 # result in a failure during negotiation as it attempts to 449 # establish a TCP connection. 450 r = SturdyRef(adave.tracker.url) 451 # highly unlikely that there's anything listening on this port 452 r.locationHints = ["127.0.0.47:1"] 453 adave.tracker.url = r.getURL() 454 return self.acarol.callRemote("set", adave) 455 d.addCallback(_introduce) 456 d.addBoth(self.shouldFail, ConnectionRefusedError, "Bad.test_location") 457 return d 458 test_location.timeout = 10 459 460 def test_hang(self): 461 f = protocol.Factory() 462 f.protocol = protocol.Protocol # ignores all input 463 p = reactor.listenTCP(0, f, interface="127.0.0.1") 464 self.createCharacters() 465 d = self.createInitialReferences() 466 d.addCallback(lambda res: self.tubA.getReference(self.dave_url)) 467 def _introduce(adave): 468 # The next form of mangling is to connect to a port which never 469 # responds, which could happen if a firewall were silently 470 # dropping the TCP packets. We can't accurately simulate this 471 # case, but we can connect to a port which accepts the connection 472 # and then stays silent. This should trigger the overall 473 # connection timeout. 474 r = SturdyRef(adave.tracker.url) 475 r.locationHints = ["127.0.0.1:%d" % p.getHost().port] 476 adave.tracker.url = r.getURL() 477 self.tubD.options['connect_timeout'] = 2 478 return self.acarol.callRemote("set", adave) 479 d.addCallback(_introduce) 480 d.addBoth(self.shouldFail, NegotiationError, "Bad.test_hang", 481 "no connection established within client timeout") 482 def _stop_listening(res): 483 d1 = p.stopListening() 484 def _done_listening(x): 485 return res 486 d1.addCallback(_done_listening) 487 return d1 488 d.addBoth(_stop_listening) 489 return d 490 test_hang.timeout = 10 491 492
