10 from client import __ver__
13 class CookieJar(dict):
16 dict.__init__(self, ())
18 def extract_cookies(self, response):
19 for cookie in response.msg.getallmatchingheaders('Set-Cookie'):
20 self.parse_cookie(cookie.strip())
21 if response.getheader('set-cookie2', None):
23 raise RuntimeError, 'Set-Cookie2', value
25 def parse_cookie(self, cookie):
28 value, attrs = cookie.split(': ', 1)[1].split(';', 1)
29 i = value.strip().split('=')
30 if len(i) == 1 and i[0] in self:
35 def get_cookie_header(self):
36 return '; '.join(('%s=%s' % i for i in self.iteritems()))
39 for k, v in self.iteritems():
45 def __init__(self, name, value):
50 class HTTPPersistentConnection(object):
51 http_class = httplib.HTTPConnection
55 def __init__(self, host, pool=None, clients_useragent=None):
56 self._conn = self.http_class(host)
58 self.last_request = time.time()
63 self.cookies = pool.cookies
65 clients_useragent = clients_useragent or ""
66 if clients_useragent != "":
67 clients_useragent += " "
68 self.useragent = clients_useragent + 'MwClient/' + __ver__ + ' (https://github.com/mwclient/mwclient)'
70 def request(self, method, host, path, headers, data,
71 raise_on_not_ok=True, auto_redirect=True):
74 if type(host) is tuple:
78 if (time.time() - self.last_request) > 60:
85 headers['Connection'] = 'Keep-Alive'
86 headers['User-Agent'] = self.useragent
87 headers['Host'] = host
88 if host in self.cookies:
89 headers['Cookie'] = self.cookies[host].get_cookie_header()
90 if issubclass(data.__class__, upload.Upload):
91 headers['Content-Type'] = data.content_type
92 headers['Content-Length'] = str(data.length)
94 headers['Content-Length'] = str(len(data))
97 headers.update(_headers)
100 self._conn.request(method, path, headers=headers)
101 if issubclass(data.__class__, upload.Upload):
105 self._conn.send(data)
107 self.last_request = time.time()
109 res = self._conn.getresponse()
110 except httplib.BadStatusLine:
113 self._conn.request(method, path, data, headers)
114 res = self._conn.getresponse()
115 except socket.error, e:
117 raise errors.HTTPError, e
118 # except Exception, e:
119 # raise errors.HTTPError, e
121 if not host in self.cookies:
122 self.cookies[host] = CookieJar()
123 self.cookies[host].extract_cookies(res)
125 if res.status >= 300 and res.status <= 399 and auto_redirect:
128 location = urlparse.urlparse(res.getheader('Location'))
129 if res.status in (302, 303):
130 if 'Content-Type' in headers:
131 del headers['Content-Type']
132 if 'Content-Length' in headers:
133 del headers['Content-Length']
139 path = path + '?' + location[4]
141 if location[0].lower() != self.scheme_name:
142 raise errors.HTTPRedirectError, ('Only HTTP connections are supported',
143 res.getheader('Location'))
145 if self.pool is None:
146 if location[1] != host:
147 raise errors.HTTPRedirectError, ('Redirecting to different hosts not supported',
148 res.getheader('Location'))
150 return self.request(method, host, path, headers, data)
152 if host == location[1] and path == old_path:
153 conn = self.__class__(location[1], self.pool)
154 self.pool.append(([location[1]], conn))
155 return self.pool.request(method, location[1], path,
156 headers, data, raise_on_not_ok, auto_redirect)
158 if res.status != 200 and raise_on_not_ok:
160 raise errors.HTTPStatusError, (res.status, res)
166 def get(self, host, path, headers=None):
167 return self.request('GET', host, path, headers, None)
169 def post(self, host, path, headers=None, data=None):
170 return self.request('POST', host, path, headers, data)
172 def head(self, host, path, headers=None, auto_redirect=False):
173 res = self.request('HEAD', host, path, headers,
174 data=None, raise_on_not_ok=False,
175 auto_redirect=auto_redirect)
177 return res.status, res.getheaders()
183 return self._conn.sock.fileno()
186 class HTTPConnection(HTTPPersistentConnection):
188 def request(self, method, host, path, headers, data,
189 raise_on_not_ok=True, auto_redirect=True):
192 headers['Connection'] = 'Close'
193 res = HTTPPersistentConnection.request(self, method, host, path, headers, data,
194 raise_on_not_ok, auto_redirect)
198 class HTTPSPersistentConnection(HTTPPersistentConnection):
199 http_class = httplib.HTTPSConnection
200 scheme_name = 'https'
203 class HTTPPool(list):
205 def __init__(self, clients_useragent=None):
208 self.clients_useragent = clients_useragent
210 def find_connection(self, host, scheme='http'):
211 if type(host) is tuple:
214 for hosts, conn in self:
215 if (scheme, host) in hosts:
218 redirected_host = None
219 for hosts, conn in self:
220 status, headers = conn.head(host, '/')
222 hosts.append((scheme, host))
224 if status >= 300 and status <= 399:
226 headers = dict(headers)
227 location = urlparse.urlparse(headers.get('location', ''))
228 if (location[0], location[1]) == (scheme, host):
229 hosts.append((scheme, host))
232 cls = HTTPPersistentConnection
233 elif scheme == 'https':
234 cls = HTTPSPersistentConnection
236 raise RuntimeError('Unsupported scheme', scheme)
237 conn = cls(host, self, self.clients_useragent)
238 self.append(([(scheme, host)], conn))
241 def get(self, host, path, headers=None):
242 return self.find_connection(host).get(host,
245 def post(self, host, path, headers=None, data=None):
246 return self.find_connection(host).post(host,
249 def head(self, host, path, headers=None, auto_redirect=False):
250 return self.find_connection(host).head(host,
251 path, headers, auto_redirect)
253 def request(self, method, host, path, headers, data,
254 raise_on_not_ok, auto_redirect):
255 return self.find_connection(host).request(method, host, path,
256 headers, data, raise_on_not_ok, auto_redirect)
259 for hosts, conn in self: