twistedその3
上位プロキシ。上位プロキシにはProxomitronを使った。
ソースは
http://lab.hde.co.jp/python/twisted/
で紹介されているものほぼそのままデス。
だいぶショートカットできました。感謝です。
from twisted.web import http, proxy from twisted.internet import reactor from twisted.python import log import urlparse PROXY_HOST='localhost' PROXY_PORT=8080 class UpperProxyRequest(proxy.ProxyRequest): def process(self): parsed = urlparse.urlparse(self.uri) protocol = parsed[0] host = parsed[1] print self.method port = self.ports[protocol] if ':' in host: host, port = host.split(':') port = int(port) rest = urlparse.urlunparse(('', '') + parsed[2:]) if not rest: rest = rest + '/' class_ = self.protocols[protocol] headers = self.getAllHeaders().copy() if 'host' not in headers: headers['host'] = host self.content.seek(0, 0) s = self.content.read() clientFactory = class_( self.method, self.uri, self.clientproto, headers, s, self) assert(PROXY_HOST) assert(PROXY_PORT) self.reactor.connectTCP(PROXY_HOST, PROXY_PORT, clientFactory) proxy.Proxy.requestFactory=UpperProxyRequest if __name__=="__main__": import sys log.startLogging(sys.stdout) f=http.HTTPFactory() f.protocol=proxy.Proxy reactor.listenTCP(10080, f) reactor.run()
+-------+ |reactor| +-------+---------+ +-----+ +-----------------+ +10080 HTTPFactory|->|Proxy|->|UpperProxyRequest| +-----------------+ +-----+ +----------------------------------+ A |process-> ProxyClientFactory| | +------------------V---------+------+ +-------+ |ProxyClient 上位Proxyから取ってくる| |browser| +-----------------------------------+ +-------+
ProxyClientが地味に優秀で直接でもProxy経由でもどっちでも取ってこれるようだ。
twistedその1
やっと使い方がわかってきたのでメモ。
twistedはイベントドリブンなネットワークフレームワークとか説明されるのだが、最初は何のことか分からんわけであります。
何かすごそうな気がするのだが妙に敷居が高い(3回は習得が頓挫している)。
twistedの部品を組み合わせてfilter串を作るまでの順を追ってみることにした。
HTTPFactory
とりあえずコード。
まずはproxyにする前に通常のHTTPアクセスに応答するサーバを作る。実行してからブラウザでhttp://localhost:10080にアクセスする。
from twisted.web import http from twisted.internet import reactor from twisted.python import log class MyRequest(http.Request): def process(self): self.write("Hello") self.finish() if __name__=="__main__": import sys log.startLogging(sys.stdout) f=http.HTTPFactory() f.protocol.requestFactory=MyRequest reactor.listenTCP(10080, f) reactor.run()
10080ポートをリッスンしてリクエストが来たらHelloとだけ返すようにした。
ヘッダまで含めると以下のように応答する。
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Hello
ちゃんとHTTP/1.1に対応しているみたいだ(proxomitronで見た)。
main部のソースの意味は最初の2行はログ出力の設定なので気にしない。
あとはFactoryを継承したクラスのオブジェクトを作って、reactorでポートを指定してListenするというtwistedの定石通りなのだが、
f.protocol.requestFactory=MyRequest
という部分が込み入っている。この辺にtwistedのとっつき難さがあらわれている。twistedを使いこなすにはリファレンスを読むだけでは不十分で、ソースを読んで適当なクラス変数をカスタムのクラスで置き換えるというまさに上記のような使い方がどうしても必要になる。個々のソースは短いので読み易いのだが、クラスとオブジェクトが絡み合っていて混乱する。紙に呼び出し関係を書いて理解しましたとも。
その1ではHTTPFactoryでHTTPサーバを作って、中身はRequest#processで何とかするという枠組みを見た。
twistedその2
ただのproxy。
from twisted.web import http, proxy from twisted.internet import reactor from twisted.python import log if __name__=="__main__": import sys log.startLogging(sys.stdout) f=http.HTTPFactory() f.protocol=proxy.Proxy reactor.listenTCP(10080, f) reactor.run()
前より簡単になってRequestを継承したクラスが消滅した。proxy.Proxy(http.HTTPChannel)がrequestFactoryとしてProxyRequestを保持しているからそのまま利用できるわけです。
+-------+ |reactor| +-------+---------+ +-----------+ +---------+ +10080 HTTPFactory|->|HTTPChannel|->|MyRequest| +-----------------+ +-----------+ +---------+-------------+ A |process->write("Hello")| | +-----------------------+ +-------+ |browser| +-------+
が
+-------+ |reactor| +-------+---------+ +-----+ +------------+ +10080 HTTPFactory|->|Proxy|->|ProxyRequest| +-----------------+ +-----+ +------------+---------------------+ A |process-> ProxyClientFactory| | +------------------V---------++ +-------+ |ProxyClient urlから取ってくる| |browser| +-----------------------------+ +-------+
となった。たぶん