Twisted を使用した Proxy のサンプルについての記録。
オリジナルは GitHub Gist で公開されている tcp-proxy.py となります。
■
Proxy を実現するには、 squid や Delegate 、 HAProxy といったものがありますが、 tcp-proxy.py は Twisted を使用した Python スクリプトとなります。
設定ファイルやコマンドライン引数などはなく、待ち受けと転送先を変更するにはそれぞれ 57 行目と 75 行目を書き換えるだけです。
#!/usr/bin/env python # coding: utf-8 # http://musta.sh/2012-03-04/twisted-tcp-proxy.html import sys from twisted.internet import defer from twisted.internet import protocol from twisted.internet import reactor from twisted.python import log class ProxyClientProtocol(protocol.Protocol): def connectionMade(self): log.msg("Client: connected to peer") self.cli_queue = self.factory.cli_queue self.cli_queue.get().addCallback(self.serverDataReceived) def serverDataReceived(self, chunk): if chunk is False: self.cli_queue = None log.msg("Client: disconnecting from peer") self.factory.continueTrying = False self.transport.loseConnection() elif self.cli_queue: log.msg("Client: writing %d bytes to peer" % len(chunk)) self.transport.write(chunk) self.cli_queue.get().addCallback(self.serverDataReceived) else: self.factory.cli_queue.put(chunk) def dataReceived(self, chunk): log.msg("Client: %d bytes received from peer" % len(chunk)) self.factory.srv_queue.put(chunk) def connectionLost(self, why): if self.cli_queue: self.cli_queue = None log.msg("Client: peer disconnected unexpectedly") class ProxyClientFactory(protocol.ReconnectingClientFactory): maxDelay = 10 continueTrying = True protocol = ProxyClientProtocol def __init__(self, srv_queue, cli_queue): self.srv_queue = srv_queue self.cli_queue = cli_queue class ProxyServer(protocol.Protocol): def connectionMade(self): self.srv_queue = defer.DeferredQueue() self.cli_queue = defer.DeferredQueue() self.srv_queue.get().addCallback(self.clientDataReceived) factory = ProxyClientFactory(self.srv_queue, self.cli_queue) reactor.connectTCP("127.0.0.1", 6666, factory) def clientDataReceived(self, chunk): log.msg("Server: writing %d bytes to original client" % len(chunk)) self.transport.write(chunk) self.srv_queue.get().addCallback(self.clientDataReceived) def dataReceived(self, chunk): log.msg("Server: %d bytes received" % len(chunk)) self.cli_queue.put(chunk) def connectionLost(self, why): self.cli_queue.put(False) if __name__ == "__main__": log.startLogging(sys.stdout) factory = protocol.Factory() factory.protocol = ProxyServer reactor.listenTCP(9999, factory, interface="0.0.0.0") reactor.run()
■
Twisted の特徴は、イベント駆動と Defferred オブジェクトである事でしょうか。この二つを使用する事でポーリング処理が不要になるのと、処理を時系列に記述する事が出来るという利点があります。
自分は Deffered を理解するのに結構時間がかかってしまいました。