[Twisted-Python] Trying to proxy through multiple IPs
Andrew Bennetts
andrew-twisted at puzzling.org
Tue Nov 4 03:04:43 MST 2008
Erik Wickstrom wrote:
> Hi all,
>
> I'm trying to write a proxy server that accepts connections on
> multiple ports, and depending on the port, using a different IP for
> the outgoing connection. My code works except that adding additional
> reactor.listenTCP(...)s overwrite the IP of the previous listenTCPs.
> So the proxy accepts connections on all the desired ports, but only
> uses the last IP address for outgoing connections.
>
> The ip (bindIP) is somehow being overwritten. Based on advice from
> IRC (exarkun --thanks!), I've tried a couple attempts at using
> closures to solve the problem, but non of my implementations have done
> the trick.
I doesn't look like you understand how to write closures in Python. Consider
this snippet of your code:
> class ProxyFactory(http.HTTPFactory):
> def class_factory(self, bindIP):
> def closure(ip):
> klass2 = ProxyRequest
> setattr(klass2, 'bindIP', ip)
> return klass2
> klass = Proxy
> setattr(klass, 'requestFactory', closure(bindIP))
> return klass
>
> def __init__(self, ip):
> http.HTTPFactory.__init__(self)
> self.ip = ip
> #self.protocol = proxy.Proxy
> self.protocol = self.class_factory(ip)
These lines are equivalent to the much simpler:
class ProxyFactory(http.HTTPFactory):
def __init__(self, ip):
self.protocol = Proxy
self.protocol.requestFactory = ProxyRequest
self.protocol.requestFactory.bindIP = ip
In particular, even though you define a function you call “closure”, because you
always invoke it immediately after defining it (and do nothing else with it) you
don't gain any difference in behaviour by making a function.
So the problem is you have just a single global requestFactory for all ProxyFactory's
(the ProxyRequest class), but you're mutating that as if it's not global.
The solution is to either,
a) actually have a different requestFactory, or
b) pass the bindIP to the ProxyRequest (the object that cares about it) some
other way.
a) is a bit messy, even when done correctly. The simpler way is b):
class ProxyRequest(Request):
protocols = {'http': ProxyClientFactory}
ports = {'http': 80}
def __init__(self, channel, queued, reactor=reactor):
Request.__init__(self, channel, queued)
self.reactor = reactor
self.bindIP = self.channel.factory.ip
# ...the rest of ProxyRequest as you had it...
class Proxy(HTTPChannel):
requestFactory = ProxyRequest
class ProxyFactory(http.HTTPFactory):
def __init__(self, ip):
http.HTTPFactory.__init__(self)
self.ip = ip
-Andrew.
More information about the Twisted-Python
mailing list