]>
Commit | Line | Data |
---|---|---|
879aa787 MT |
1 | # Written by John Hoffman |
2 | # see LICENSE.txt for license information | |
3 | ||
4 | from httplib import HTTPConnection, HTTPSConnection, HTTPException | |
5 | from urlparse import urlparse | |
6 | from bencode import bdecode | |
7 | import socket | |
8 | from gzip import GzipFile | |
9 | from StringIO import StringIO | |
10 | from urllib import quote, unquote | |
11 | from __init__ import product_name, version_short | |
12 | ||
13 | VERSION = product_name+'/'+version_short | |
14 | MAX_REDIRECTS = 10 | |
15 | ||
16 | ||
17 | class btHTTPcon(HTTPConnection): # attempt to add automatic connection timeout | |
18 | def connect(self): | |
19 | HTTPConnection.connect(self) | |
20 | try: | |
21 | self.sock.settimeout(30) | |
22 | except: | |
23 | pass | |
24 | ||
25 | class btHTTPScon(HTTPSConnection): # attempt to add automatic connection timeout | |
26 | def connect(self): | |
27 | HTTPSConnection.connect(self) | |
28 | try: | |
29 | self.sock.settimeout(30) | |
30 | except: | |
31 | pass | |
32 | ||
33 | class urlopen: | |
34 | def __init__(self, url): | |
35 | self.tries = 0 | |
36 | self._open(url.strip()) | |
37 | self.error_return = None | |
38 | ||
39 | def _open(self, url): | |
40 | self.tries += 1 | |
41 | if self.tries > MAX_REDIRECTS: | |
42 | raise IOError, ('http error', 500, | |
43 | "Internal Server Error: Redirect Recursion") | |
44 | (scheme, netloc, path, pars, query, fragment) = urlparse(url) | |
45 | if scheme != 'http' and scheme != 'https': | |
46 | raise IOError, ('url error', 'unknown url type', scheme, url) | |
47 | url = path | |
48 | if pars: | |
49 | url += ';'+pars | |
50 | if query: | |
51 | url += '?'+query | |
52 | # if fragment: | |
53 | try: | |
54 | if scheme == 'http': | |
55 | self.connection = btHTTPcon(netloc) | |
56 | else: | |
57 | self.connection = btHTTPScon(netloc) | |
58 | self.connection.request('GET', url, None, | |
59 | { 'User-Agent': VERSION, | |
60 | 'Accept-Encoding': 'gzip' } ) | |
61 | self.response = self.connection.getresponse() | |
62 | except HTTPException, e: | |
63 | raise IOError, ('http error', str(e)) | |
64 | status = self.response.status | |
65 | if status in (301,302): | |
66 | try: | |
67 | self.connection.close() | |
68 | except: | |
69 | pass | |
70 | self._open(self.response.getheader('Location')) | |
71 | return | |
72 | if status != 200: | |
73 | try: | |
74 | data = self._read() | |
75 | d = bdecode(data) | |
76 | if d.has_key('failure reason'): | |
77 | self.error_return = data | |
78 | return | |
79 | except: | |
80 | pass | |
81 | raise IOError, ('http error', status, self.response.reason) | |
82 | ||
83 | def read(self): | |
84 | if self.error_return: | |
85 | return self.error_return | |
86 | return self._read() | |
87 | ||
88 | def _read(self): | |
89 | data = self.response.read() | |
90 | if self.response.getheader('Content-Encoding','').find('gzip') >= 0: | |
91 | try: | |
92 | compressed = StringIO(data) | |
93 | f = GzipFile(fileobj = compressed) | |
94 | data = f.read() | |
95 | except: | |
96 | raise IOError, ('http error', 'got corrupt response') | |
97 | return data | |
98 | ||
99 | def close(self): | |
100 | self.connection.close() |