1
0

SaneThreadedResolver.py 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. # Just like ThreadedResolver, but doesn't suck
  2. #
  3. # The contents of this file are subject to the Python Software Foundation
  4. # License Version 2.3 (the License). You may not copy or use this file, in
  5. # either source code or executable form, except in compliance with the License.
  6. # You may obtain a copy of the License at http://www.python.org/license.
  7. #
  8. # Software distributed under the License is distributed on an AS IS basis,
  9. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  10. # for the specific language governing rights and limitations under the
  11. # License.
  12. #
  13. # by Greg Hazel
  14. import socket
  15. import operator
  16. from twisted.internet import error, defer, threads
  17. from twisted.python import failure
  18. class SaneThreadedResolver:
  19. # I won't do this. Zope.interface sucks.
  20. #implements(IResolverSimple)
  21. def __init__(self, reactor):
  22. self.reactor = reactor
  23. self._runningQueries = {}
  24. def _fail(self, name, err):
  25. err = error.DNSLookupError("address %r not found: %s" % (name, err))
  26. return failure.Failure(err)
  27. def _checkTimeout(self, result, name, userDeferred):
  28. if userDeferred in self._runningQueries:
  29. cancelCall = self._runningQueries.pop(userDeferred)
  30. cancelCall.cancel()
  31. if userDeferred.called:
  32. return
  33. if isinstance(result, failure.Failure):
  34. userDeferred.errback(self._fail(name, result.getErrorMessage()))
  35. else:
  36. userDeferred.callback(result)
  37. def _doGetHostByName(self, name, onStart):
  38. self.reactor.callFromThread(onStart)
  39. return socket.gethostbyname(name)
  40. def getHostByName(self, name, timeout = (1, 3, 11, 45)):
  41. if timeout:
  42. timeoutDelay = reduce(operator.add, timeout)
  43. else:
  44. timeoutDelay = 60
  45. userDeferred = defer.Deferred()
  46. def _onStart():
  47. cancelCall = self.reactor.callLater(
  48. timeoutDelay, self._checkTimeout,
  49. self._fail(name, "timeout error"), name, userDeferred)
  50. self._runningQueries[userDeferred] = cancelCall
  51. lookupDeferred = threads.deferToThread(self._doGetHostByName, name, _onStart)
  52. lookupDeferred.addBoth(self._checkTimeout, name, userDeferred)
  53. return userDeferred