pyuvを使って http-clientを書いてみる
どうもこんばんは。差し迫ったなにかがあるときって、別のなにかをやりたくなるときってありますよね。
近頃良く遊んでるpyuvでhttpクライアントを作ってみました。
ソースコード
エラー処理が抜けてます。
import sys import socket import urlparse import pyuv loop, clients = None, [] def on_close(tcp_handle): tcp_handle.data["file"].close() def on_read(tcp_handle, data, error): if data is None: tcp_handle.close(on_close) return if not data.strip(): return if tcp_handle.data['header'] == '': pos = data.find(b'\r\n\r\n') if pos > -1: tcp_handle.data['header'] = data[0:pos] tcp_handle.data["file"].write(data[pos+4:]) else: tcp_handle.data["file"].write(data) def on_write(tcp_handle, status): tcp_handle.start_read(on_read) def on_connection(tcp_handle, status): print("[connect]") req = 'GET %s HTTP/1.0\nHost: %s\n\n' % (tcp_handle.data["url"].path,tcp_handle.data["url"].hostname) tcp_handle.write(req, on_write) def getaddrinfo_cb_maker(index): ''' urlとipの関係を維持するためにindexで関連づけする ''' def getaddrinfo_cb(resolver, status, result): parsed = resolver.data["urls"][index] l = pyuv.Loop.default_loop() client = pyuv.TCP(l) client.data={'url': parsed, 'header': b'', 'file': open(parsed.path.split('/')[-1], 'wb')} ip = result[0][-1][0] client.connect((ip, 80),on_connection) l.run() return getaddrinfo_cb def main(arg): loop = pyuv.Loop.default_loop() resolver = pyuv.dns.DNSResolver(loop) resolver.data = {"urls":[]} idx = 0 for url in arg: parsed = urlparse.urlparse(url) resolver.data["urls"].append(parsed) resolver.getaddrinfo(getaddrinfo_cb_maker(idx), parsed.hostname, 80) idx += 1 loop.run() if __name__ == "__main__": print("PyUV version %s" % pyuv.__version__) # python wget2.py url url ... main(sys.argv[1:]) print("Stopped!")
実行
こんなかんじでファイルを2つ同時にダウンロードできます。
ダウンロード後にmd5sumコマンドを実行してファイルが正しいかを調べられると本当はいいのかも。。
$ time python wget2.py http://pypi.python.org/packages/source/D/Django/Django-1.3.1.tar.gz http://pypi.python.org/packages/source/i/ipython/ipython-0.12.tar.gz#md5=0199b62aa65986726b46247db3cb06cf PyUV version 0.2.0 82.94.164.168 82.94.164.168 [connect] GET /packages/source/i/ipython/ipython-0.12.tar.gz HTTP/1.0 Host: pypi.python.org [connect] GET /packages/source/D/Django/Django-1.3.1.tar.gz HTTP/1.0 Host: pypi.python.org Stopped! real 0m30.623s user 0m0.200s sys 0m0.992s
所感
コールバックが多すぎなので、コードの見通しが悪いのが気になる。
コードの再利用はわりと難しいな。
Twistedとかgeventだとどう書けるのか、とても興味があります。