##// END OF EJS Templates
url: use absolute_import
Gregory Szorc -
r25990:c3efcdba default
parent child Browse files
Show More
@@ -1,508 +1,521 b''
1 # url.py - HTTP handling for mercurial
1 # url.py - HTTP handling for mercurial
2 #
2 #
3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 import urllib, urllib2, httplib, os, socket, cStringIO, base64
10 from __future__ import absolute_import
11 from i18n import _
11
12 import keepalive, util, sslutil
12 import base64
13 import httpconnection as httpconnectionmod
13 import cStringIO
14 import httplib
15 import os
16 import socket
17 import urllib
18 import urllib2
19
20 from .i18n import _
21 from . import (
22 httpconnection as httpconnectionmod,
23 keepalive,
24 sslutil,
25 util,
26 )
14
27
15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
28 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
16 def __init__(self, ui):
29 def __init__(self, ui):
17 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
30 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
18 self.ui = ui
31 self.ui = ui
19
32
20 def find_user_password(self, realm, authuri):
33 def find_user_password(self, realm, authuri):
21 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
34 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
22 self, realm, authuri)
35 self, realm, authuri)
23 user, passwd = authinfo
36 user, passwd = authinfo
24 if user and passwd:
37 if user and passwd:
25 self._writedebug(user, passwd)
38 self._writedebug(user, passwd)
26 return (user, passwd)
39 return (user, passwd)
27
40
28 if not user or not passwd:
41 if not user or not passwd:
29 res = httpconnectionmod.readauthforuri(self.ui, authuri, user)
42 res = httpconnectionmod.readauthforuri(self.ui, authuri, user)
30 if res:
43 if res:
31 group, auth = res
44 group, auth = res
32 user, passwd = auth.get('username'), auth.get('password')
45 user, passwd = auth.get('username'), auth.get('password')
33 self.ui.debug("using auth.%s.* for authentication\n" % group)
46 self.ui.debug("using auth.%s.* for authentication\n" % group)
34 if not user or not passwd:
47 if not user or not passwd:
35 u = util.url(authuri)
48 u = util.url(authuri)
36 u.query = None
49 u.query = None
37 if not self.ui.interactive():
50 if not self.ui.interactive():
38 raise util.Abort(_('http authorization required for %s') %
51 raise util.Abort(_('http authorization required for %s') %
39 util.hidepassword(str(u)))
52 util.hidepassword(str(u)))
40
53
41 self.ui.write(_("http authorization required for %s\n") %
54 self.ui.write(_("http authorization required for %s\n") %
42 util.hidepassword(str(u)))
55 util.hidepassword(str(u)))
43 self.ui.write(_("realm: %s\n") % realm)
56 self.ui.write(_("realm: %s\n") % realm)
44 if user:
57 if user:
45 self.ui.write(_("user: %s\n") % user)
58 self.ui.write(_("user: %s\n") % user)
46 else:
59 else:
47 user = self.ui.prompt(_("user:"), default=None)
60 user = self.ui.prompt(_("user:"), default=None)
48
61
49 if not passwd:
62 if not passwd:
50 passwd = self.ui.getpass()
63 passwd = self.ui.getpass()
51
64
52 self.add_password(realm, authuri, user, passwd)
65 self.add_password(realm, authuri, user, passwd)
53 self._writedebug(user, passwd)
66 self._writedebug(user, passwd)
54 return (user, passwd)
67 return (user, passwd)
55
68
56 def _writedebug(self, user, passwd):
69 def _writedebug(self, user, passwd):
57 msg = _('http auth: user %s, password %s\n')
70 msg = _('http auth: user %s, password %s\n')
58 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
71 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
59
72
60 def find_stored_password(self, authuri):
73 def find_stored_password(self, authuri):
61 return urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
74 return urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
62 self, None, authuri)
75 self, None, authuri)
63
76
64 class proxyhandler(urllib2.ProxyHandler):
77 class proxyhandler(urllib2.ProxyHandler):
65 def __init__(self, ui):
78 def __init__(self, ui):
66 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
79 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
67 # XXX proxyauthinfo = None
80 # XXX proxyauthinfo = None
68
81
69 if proxyurl:
82 if proxyurl:
70 # proxy can be proper url or host[:port]
83 # proxy can be proper url or host[:port]
71 if not (proxyurl.startswith('http:') or
84 if not (proxyurl.startswith('http:') or
72 proxyurl.startswith('https:')):
85 proxyurl.startswith('https:')):
73 proxyurl = 'http://' + proxyurl + '/'
86 proxyurl = 'http://' + proxyurl + '/'
74 proxy = util.url(proxyurl)
87 proxy = util.url(proxyurl)
75 if not proxy.user:
88 if not proxy.user:
76 proxy.user = ui.config("http_proxy", "user")
89 proxy.user = ui.config("http_proxy", "user")
77 proxy.passwd = ui.config("http_proxy", "passwd")
90 proxy.passwd = ui.config("http_proxy", "passwd")
78
91
79 # see if we should use a proxy for this url
92 # see if we should use a proxy for this url
80 no_list = ["localhost", "127.0.0.1"]
93 no_list = ["localhost", "127.0.0.1"]
81 no_list.extend([p.lower() for
94 no_list.extend([p.lower() for
82 p in ui.configlist("http_proxy", "no")])
95 p in ui.configlist("http_proxy", "no")])
83 no_list.extend([p.strip().lower() for
96 no_list.extend([p.strip().lower() for
84 p in os.getenv("no_proxy", '').split(',')
97 p in os.getenv("no_proxy", '').split(',')
85 if p.strip()])
98 if p.strip()])
86 # "http_proxy.always" config is for running tests on localhost
99 # "http_proxy.always" config is for running tests on localhost
87 if ui.configbool("http_proxy", "always"):
100 if ui.configbool("http_proxy", "always"):
88 self.no_list = []
101 self.no_list = []
89 else:
102 else:
90 self.no_list = no_list
103 self.no_list = no_list
91
104
92 proxyurl = str(proxy)
105 proxyurl = str(proxy)
93 proxies = {'http': proxyurl, 'https': proxyurl}
106 proxies = {'http': proxyurl, 'https': proxyurl}
94 ui.debug('proxying through http://%s:%s\n' %
107 ui.debug('proxying through http://%s:%s\n' %
95 (proxy.host, proxy.port))
108 (proxy.host, proxy.port))
96 else:
109 else:
97 proxies = {}
110 proxies = {}
98
111
99 # urllib2 takes proxy values from the environment and those
112 # urllib2 takes proxy values from the environment and those
100 # will take precedence if found. So, if there's a config entry
113 # will take precedence if found. So, if there's a config entry
101 # defining a proxy, drop the environment ones
114 # defining a proxy, drop the environment ones
102 if ui.config("http_proxy", "host"):
115 if ui.config("http_proxy", "host"):
103 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
116 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
104 try:
117 try:
105 if env in os.environ:
118 if env in os.environ:
106 del os.environ[env]
119 del os.environ[env]
107 except OSError:
120 except OSError:
108 pass
121 pass
109
122
110 urllib2.ProxyHandler.__init__(self, proxies)
123 urllib2.ProxyHandler.__init__(self, proxies)
111 self.ui = ui
124 self.ui = ui
112
125
113 def proxy_open(self, req, proxy, type_):
126 def proxy_open(self, req, proxy, type_):
114 host = req.get_host().split(':')[0]
127 host = req.get_host().split(':')[0]
115 for e in self.no_list:
128 for e in self.no_list:
116 if host == e:
129 if host == e:
117 return None
130 return None
118 if e.startswith('*.') and host.endswith(e[2:]):
131 if e.startswith('*.') and host.endswith(e[2:]):
119 return None
132 return None
120 if e.startswith('.') and host.endswith(e[1:]):
133 if e.startswith('.') and host.endswith(e[1:]):
121 return None
134 return None
122
135
123 return urllib2.ProxyHandler.proxy_open(self, req, proxy, type_)
136 return urllib2.ProxyHandler.proxy_open(self, req, proxy, type_)
124
137
125 def _gen_sendfile(orgsend):
138 def _gen_sendfile(orgsend):
126 def _sendfile(self, data):
139 def _sendfile(self, data):
127 # send a file
140 # send a file
128 if isinstance(data, httpconnectionmod.httpsendfile):
141 if isinstance(data, httpconnectionmod.httpsendfile):
129 # if auth required, some data sent twice, so rewind here
142 # if auth required, some data sent twice, so rewind here
130 data.seek(0)
143 data.seek(0)
131 for chunk in util.filechunkiter(data):
144 for chunk in util.filechunkiter(data):
132 orgsend(self, chunk)
145 orgsend(self, chunk)
133 else:
146 else:
134 orgsend(self, data)
147 orgsend(self, data)
135 return _sendfile
148 return _sendfile
136
149
137 has_https = util.safehasattr(urllib2, 'HTTPSHandler')
150 has_https = util.safehasattr(urllib2, 'HTTPSHandler')
138 if has_https:
151 if has_https:
139 try:
152 try:
140 _create_connection = socket.create_connection
153 _create_connection = socket.create_connection
141 except AttributeError:
154 except AttributeError:
142 _GLOBAL_DEFAULT_TIMEOUT = object()
155 _GLOBAL_DEFAULT_TIMEOUT = object()
143
156
144 def _create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
157 def _create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
145 source_address=None):
158 source_address=None):
146 # lifted from Python 2.6
159 # lifted from Python 2.6
147
160
148 msg = "getaddrinfo returns an empty list"
161 msg = "getaddrinfo returns an empty list"
149 host, port = address
162 host, port = address
150 for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
163 for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
151 af, socktype, proto, canonname, sa = res
164 af, socktype, proto, canonname, sa = res
152 sock = None
165 sock = None
153 try:
166 try:
154 sock = socket.socket(af, socktype, proto)
167 sock = socket.socket(af, socktype, proto)
155 if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
168 if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
156 sock.settimeout(timeout)
169 sock.settimeout(timeout)
157 if source_address:
170 if source_address:
158 sock.bind(source_address)
171 sock.bind(source_address)
159 sock.connect(sa)
172 sock.connect(sa)
160 return sock
173 return sock
161
174
162 except socket.error as msg:
175 except socket.error as msg:
163 if sock is not None:
176 if sock is not None:
164 sock.close()
177 sock.close()
165
178
166 raise socket.error(msg)
179 raise socket.error(msg)
167
180
168 class httpconnection(keepalive.HTTPConnection):
181 class httpconnection(keepalive.HTTPConnection):
169 # must be able to send big bundle as stream.
182 # must be able to send big bundle as stream.
170 send = _gen_sendfile(keepalive.HTTPConnection.send)
183 send = _gen_sendfile(keepalive.HTTPConnection.send)
171
184
172 def connect(self):
185 def connect(self):
173 if has_https and self.realhostport: # use CONNECT proxy
186 if has_https and self.realhostport: # use CONNECT proxy
174 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
187 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
175 self.sock.connect((self.host, self.port))
188 self.sock.connect((self.host, self.port))
176 if _generic_proxytunnel(self):
189 if _generic_proxytunnel(self):
177 # we do not support client X.509 certificates
190 # we do not support client X.509 certificates
178 self.sock = sslutil.wrapsocket(self.sock, None, None, None,
191 self.sock = sslutil.wrapsocket(self.sock, None, None, None,
179 serverhostname=self.host)
192 serverhostname=self.host)
180 else:
193 else:
181 keepalive.HTTPConnection.connect(self)
194 keepalive.HTTPConnection.connect(self)
182
195
183 def getresponse(self):
196 def getresponse(self):
184 proxyres = getattr(self, 'proxyres', None)
197 proxyres = getattr(self, 'proxyres', None)
185 if proxyres:
198 if proxyres:
186 if proxyres.will_close:
199 if proxyres.will_close:
187 self.close()
200 self.close()
188 self.proxyres = None
201 self.proxyres = None
189 return proxyres
202 return proxyres
190 return keepalive.HTTPConnection.getresponse(self)
203 return keepalive.HTTPConnection.getresponse(self)
191
204
192 # general transaction handler to support different ways to handle
205 # general transaction handler to support different ways to handle
193 # HTTPS proxying before and after Python 2.6.3.
206 # HTTPS proxying before and after Python 2.6.3.
194 def _generic_start_transaction(handler, h, req):
207 def _generic_start_transaction(handler, h, req):
195 tunnel_host = getattr(req, '_tunnel_host', None)
208 tunnel_host = getattr(req, '_tunnel_host', None)
196 if tunnel_host:
209 if tunnel_host:
197 if tunnel_host[:7] not in ['http://', 'https:/']:
210 if tunnel_host[:7] not in ['http://', 'https:/']:
198 tunnel_host = 'https://' + tunnel_host
211 tunnel_host = 'https://' + tunnel_host
199 new_tunnel = True
212 new_tunnel = True
200 else:
213 else:
201 tunnel_host = req.get_selector()
214 tunnel_host = req.get_selector()
202 new_tunnel = False
215 new_tunnel = False
203
216
204 if new_tunnel or tunnel_host == req.get_full_url(): # has proxy
217 if new_tunnel or tunnel_host == req.get_full_url(): # has proxy
205 u = util.url(tunnel_host)
218 u = util.url(tunnel_host)
206 if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS
219 if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS
207 h.realhostport = ':'.join([u.host, (u.port or '443')])
220 h.realhostport = ':'.join([u.host, (u.port or '443')])
208 h.headers = req.headers.copy()
221 h.headers = req.headers.copy()
209 h.headers.update(handler.parent.addheaders)
222 h.headers.update(handler.parent.addheaders)
210 return
223 return
211
224
212 h.realhostport = None
225 h.realhostport = None
213 h.headers = None
226 h.headers = None
214
227
215 def _generic_proxytunnel(self):
228 def _generic_proxytunnel(self):
216 proxyheaders = dict(
229 proxyheaders = dict(
217 [(x, self.headers[x]) for x in self.headers
230 [(x, self.headers[x]) for x in self.headers
218 if x.lower().startswith('proxy-')])
231 if x.lower().startswith('proxy-')])
219 self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport)
232 self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport)
220 for header in proxyheaders.iteritems():
233 for header in proxyheaders.iteritems():
221 self.send('%s: %s\r\n' % header)
234 self.send('%s: %s\r\n' % header)
222 self.send('\r\n')
235 self.send('\r\n')
223
236
224 # majority of the following code is duplicated from
237 # majority of the following code is duplicated from
225 # httplib.HTTPConnection as there are no adequate places to
238 # httplib.HTTPConnection as there are no adequate places to
226 # override functions to provide the needed functionality
239 # override functions to provide the needed functionality
227 res = self.response_class(self.sock,
240 res = self.response_class(self.sock,
228 strict=self.strict,
241 strict=self.strict,
229 method=self._method)
242 method=self._method)
230
243
231 while True:
244 while True:
232 version, status, reason = res._read_status()
245 version, status, reason = res._read_status()
233 if status != httplib.CONTINUE:
246 if status != httplib.CONTINUE:
234 break
247 break
235 while True:
248 while True:
236 skip = res.fp.readline().strip()
249 skip = res.fp.readline().strip()
237 if not skip:
250 if not skip:
238 break
251 break
239 res.status = status
252 res.status = status
240 res.reason = reason.strip()
253 res.reason = reason.strip()
241
254
242 if res.status == 200:
255 if res.status == 200:
243 while True:
256 while True:
244 line = res.fp.readline()
257 line = res.fp.readline()
245 if line == '\r\n':
258 if line == '\r\n':
246 break
259 break
247 return True
260 return True
248
261
249 if version == 'HTTP/1.0':
262 if version == 'HTTP/1.0':
250 res.version = 10
263 res.version = 10
251 elif version.startswith('HTTP/1.'):
264 elif version.startswith('HTTP/1.'):
252 res.version = 11
265 res.version = 11
253 elif version == 'HTTP/0.9':
266 elif version == 'HTTP/0.9':
254 res.version = 9
267 res.version = 9
255 else:
268 else:
256 raise httplib.UnknownProtocol(version)
269 raise httplib.UnknownProtocol(version)
257
270
258 if res.version == 9:
271 if res.version == 9:
259 res.length = None
272 res.length = None
260 res.chunked = 0
273 res.chunked = 0
261 res.will_close = 1
274 res.will_close = 1
262 res.msg = httplib.HTTPMessage(cStringIO.StringIO())
275 res.msg = httplib.HTTPMessage(cStringIO.StringIO())
263 return False
276 return False
264
277
265 res.msg = httplib.HTTPMessage(res.fp)
278 res.msg = httplib.HTTPMessage(res.fp)
266 res.msg.fp = None
279 res.msg.fp = None
267
280
268 # are we using the chunked-style of transfer encoding?
281 # are we using the chunked-style of transfer encoding?
269 trenc = res.msg.getheader('transfer-encoding')
282 trenc = res.msg.getheader('transfer-encoding')
270 if trenc and trenc.lower() == "chunked":
283 if trenc and trenc.lower() == "chunked":
271 res.chunked = 1
284 res.chunked = 1
272 res.chunk_left = None
285 res.chunk_left = None
273 else:
286 else:
274 res.chunked = 0
287 res.chunked = 0
275
288
276 # will the connection close at the end of the response?
289 # will the connection close at the end of the response?
277 res.will_close = res._check_close()
290 res.will_close = res._check_close()
278
291
279 # do we have a Content-Length?
292 # do we have a Content-Length?
280 # NOTE: RFC 2616, section 4.4, #3 says we ignore this if
293 # NOTE: RFC 2616, section 4.4, #3 says we ignore this if
281 # transfer-encoding is "chunked"
294 # transfer-encoding is "chunked"
282 length = res.msg.getheader('content-length')
295 length = res.msg.getheader('content-length')
283 if length and not res.chunked:
296 if length and not res.chunked:
284 try:
297 try:
285 res.length = int(length)
298 res.length = int(length)
286 except ValueError:
299 except ValueError:
287 res.length = None
300 res.length = None
288 else:
301 else:
289 if res.length < 0: # ignore nonsensical negative lengths
302 if res.length < 0: # ignore nonsensical negative lengths
290 res.length = None
303 res.length = None
291 else:
304 else:
292 res.length = None
305 res.length = None
293
306
294 # does the body have a fixed length? (of zero)
307 # does the body have a fixed length? (of zero)
295 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
308 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
296 100 <= status < 200 or # 1xx codes
309 100 <= status < 200 or # 1xx codes
297 res._method == 'HEAD'):
310 res._method == 'HEAD'):
298 res.length = 0
311 res.length = 0
299
312
300 # if the connection remains open, and we aren't using chunked, and
313 # if the connection remains open, and we aren't using chunked, and
301 # a content-length was not provided, then assume that the connection
314 # a content-length was not provided, then assume that the connection
302 # WILL close.
315 # WILL close.
303 if (not res.will_close and
316 if (not res.will_close and
304 not res.chunked and
317 not res.chunked and
305 res.length is None):
318 res.length is None):
306 res.will_close = 1
319 res.will_close = 1
307
320
308 self.proxyres = res
321 self.proxyres = res
309
322
310 return False
323 return False
311
324
312 class httphandler(keepalive.HTTPHandler):
325 class httphandler(keepalive.HTTPHandler):
313 def http_open(self, req):
326 def http_open(self, req):
314 return self.do_open(httpconnection, req)
327 return self.do_open(httpconnection, req)
315
328
316 def _start_transaction(self, h, req):
329 def _start_transaction(self, h, req):
317 _generic_start_transaction(self, h, req)
330 _generic_start_transaction(self, h, req)
318 return keepalive.HTTPHandler._start_transaction(self, h, req)
331 return keepalive.HTTPHandler._start_transaction(self, h, req)
319
332
320 if has_https:
333 if has_https:
321 class httpsconnection(httplib.HTTPConnection):
334 class httpsconnection(httplib.HTTPConnection):
322 response_class = keepalive.HTTPResponse
335 response_class = keepalive.HTTPResponse
323 default_port = httplib.HTTPS_PORT
336 default_port = httplib.HTTPS_PORT
324 # must be able to send big bundle as stream.
337 # must be able to send big bundle as stream.
325 send = _gen_sendfile(keepalive.safesend)
338 send = _gen_sendfile(keepalive.safesend)
326 getresponse = keepalive.wrapgetresponse(httplib.HTTPConnection)
339 getresponse = keepalive.wrapgetresponse(httplib.HTTPConnection)
327
340
328 def __init__(self, host, port=None, key_file=None, cert_file=None,
341 def __init__(self, host, port=None, key_file=None, cert_file=None,
329 *args, **kwargs):
342 *args, **kwargs):
330 httplib.HTTPConnection.__init__(self, host, port, *args, **kwargs)
343 httplib.HTTPConnection.__init__(self, host, port, *args, **kwargs)
331 self.key_file = key_file
344 self.key_file = key_file
332 self.cert_file = cert_file
345 self.cert_file = cert_file
333
346
334 def connect(self):
347 def connect(self):
335 self.sock = _create_connection((self.host, self.port))
348 self.sock = _create_connection((self.host, self.port))
336
349
337 host = self.host
350 host = self.host
338 if self.realhostport: # use CONNECT proxy
351 if self.realhostport: # use CONNECT proxy
339 _generic_proxytunnel(self)
352 _generic_proxytunnel(self)
340 host = self.realhostport.rsplit(':', 1)[0]
353 host = self.realhostport.rsplit(':', 1)[0]
341 self.sock = sslutil.wrapsocket(
354 self.sock = sslutil.wrapsocket(
342 self.sock, self.key_file, self.cert_file, serverhostname=host,
355 self.sock, self.key_file, self.cert_file, serverhostname=host,
343 **sslutil.sslkwargs(self.ui, host))
356 **sslutil.sslkwargs(self.ui, host))
344 sslutil.validator(self.ui, host)(self.sock)
357 sslutil.validator(self.ui, host)(self.sock)
345
358
346 class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
359 class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
347 def __init__(self, ui):
360 def __init__(self, ui):
348 keepalive.KeepAliveHandler.__init__(self)
361 keepalive.KeepAliveHandler.__init__(self)
349 urllib2.HTTPSHandler.__init__(self)
362 urllib2.HTTPSHandler.__init__(self)
350 self.ui = ui
363 self.ui = ui
351 self.pwmgr = passwordmgr(self.ui)
364 self.pwmgr = passwordmgr(self.ui)
352
365
353 def _start_transaction(self, h, req):
366 def _start_transaction(self, h, req):
354 _generic_start_transaction(self, h, req)
367 _generic_start_transaction(self, h, req)
355 return keepalive.KeepAliveHandler._start_transaction(self, h, req)
368 return keepalive.KeepAliveHandler._start_transaction(self, h, req)
356
369
357 def https_open(self, req):
370 def https_open(self, req):
358 # req.get_full_url() does not contain credentials and we may
371 # req.get_full_url() does not contain credentials and we may
359 # need them to match the certificates.
372 # need them to match the certificates.
360 url = req.get_full_url()
373 url = req.get_full_url()
361 user, password = self.pwmgr.find_stored_password(url)
374 user, password = self.pwmgr.find_stored_password(url)
362 res = httpconnectionmod.readauthforuri(self.ui, url, user)
375 res = httpconnectionmod.readauthforuri(self.ui, url, user)
363 if res:
376 if res:
364 group, auth = res
377 group, auth = res
365 self.auth = auth
378 self.auth = auth
366 self.ui.debug("using auth.%s.* for authentication\n" % group)
379 self.ui.debug("using auth.%s.* for authentication\n" % group)
367 else:
380 else:
368 self.auth = None
381 self.auth = None
369 return self.do_open(self._makeconnection, req)
382 return self.do_open(self._makeconnection, req)
370
383
371 def _makeconnection(self, host, port=None, *args, **kwargs):
384 def _makeconnection(self, host, port=None, *args, **kwargs):
372 keyfile = None
385 keyfile = None
373 certfile = None
386 certfile = None
374
387
375 if len(args) >= 1: # key_file
388 if len(args) >= 1: # key_file
376 keyfile = args[0]
389 keyfile = args[0]
377 if len(args) >= 2: # cert_file
390 if len(args) >= 2: # cert_file
378 certfile = args[1]
391 certfile = args[1]
379 args = args[2:]
392 args = args[2:]
380
393
381 # if the user has specified different key/cert files in
394 # if the user has specified different key/cert files in
382 # hgrc, we prefer these
395 # hgrc, we prefer these
383 if self.auth and 'key' in self.auth and 'cert' in self.auth:
396 if self.auth and 'key' in self.auth and 'cert' in self.auth:
384 keyfile = self.auth['key']
397 keyfile = self.auth['key']
385 certfile = self.auth['cert']
398 certfile = self.auth['cert']
386
399
387 conn = httpsconnection(host, port, keyfile, certfile, *args,
400 conn = httpsconnection(host, port, keyfile, certfile, *args,
388 **kwargs)
401 **kwargs)
389 conn.ui = self.ui
402 conn.ui = self.ui
390 return conn
403 return conn
391
404
392 class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler):
405 class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler):
393 def __init__(self, *args, **kwargs):
406 def __init__(self, *args, **kwargs):
394 urllib2.HTTPDigestAuthHandler.__init__(self, *args, **kwargs)
407 urllib2.HTTPDigestAuthHandler.__init__(self, *args, **kwargs)
395 self.retried_req = None
408 self.retried_req = None
396
409
397 def reset_retry_count(self):
410 def reset_retry_count(self):
398 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
411 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
399 # forever. We disable reset_retry_count completely and reset in
412 # forever. We disable reset_retry_count completely and reset in
400 # http_error_auth_reqed instead.
413 # http_error_auth_reqed instead.
401 pass
414 pass
402
415
403 def http_error_auth_reqed(self, auth_header, host, req, headers):
416 def http_error_auth_reqed(self, auth_header, host, req, headers):
404 # Reset the retry counter once for each request.
417 # Reset the retry counter once for each request.
405 if req is not self.retried_req:
418 if req is not self.retried_req:
406 self.retried_req = req
419 self.retried_req = req
407 self.retried = 0
420 self.retried = 0
408 # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
421 # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
409 # it doesn't know about the auth type requested. This can happen if
422 # it doesn't know about the auth type requested. This can happen if
410 # somebody is using BasicAuth and types a bad password.
423 # somebody is using BasicAuth and types a bad password.
411 try:
424 try:
412 return urllib2.HTTPDigestAuthHandler.http_error_auth_reqed(
425 return urllib2.HTTPDigestAuthHandler.http_error_auth_reqed(
413 self, auth_header, host, req, headers)
426 self, auth_header, host, req, headers)
414 except ValueError as inst:
427 except ValueError as inst:
415 arg = inst.args[0]
428 arg = inst.args[0]
416 if arg.startswith("AbstractDigestAuthHandler doesn't know "):
429 if arg.startswith("AbstractDigestAuthHandler doesn't know "):
417 return
430 return
418 raise
431 raise
419
432
420 class httpbasicauthhandler(urllib2.HTTPBasicAuthHandler):
433 class httpbasicauthhandler(urllib2.HTTPBasicAuthHandler):
421 def __init__(self, *args, **kwargs):
434 def __init__(self, *args, **kwargs):
422 self.auth = None
435 self.auth = None
423 urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
436 urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
424 self.retried_req = None
437 self.retried_req = None
425
438
426 def http_request(self, request):
439 def http_request(self, request):
427 if self.auth:
440 if self.auth:
428 request.add_unredirected_header(self.auth_header, self.auth)
441 request.add_unredirected_header(self.auth_header, self.auth)
429
442
430 return request
443 return request
431
444
432 def https_request(self, request):
445 def https_request(self, request):
433 if self.auth:
446 if self.auth:
434 request.add_unredirected_header(self.auth_header, self.auth)
447 request.add_unredirected_header(self.auth_header, self.auth)
435
448
436 return request
449 return request
437
450
438 def reset_retry_count(self):
451 def reset_retry_count(self):
439 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
452 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
440 # forever. We disable reset_retry_count completely and reset in
453 # forever. We disable reset_retry_count completely and reset in
441 # http_error_auth_reqed instead.
454 # http_error_auth_reqed instead.
442 pass
455 pass
443
456
444 def http_error_auth_reqed(self, auth_header, host, req, headers):
457 def http_error_auth_reqed(self, auth_header, host, req, headers):
445 # Reset the retry counter once for each request.
458 # Reset the retry counter once for each request.
446 if req is not self.retried_req:
459 if req is not self.retried_req:
447 self.retried_req = req
460 self.retried_req = req
448 self.retried = 0
461 self.retried = 0
449 return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
462 return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
450 self, auth_header, host, req, headers)
463 self, auth_header, host, req, headers)
451
464
452 def retry_http_basic_auth(self, host, req, realm):
465 def retry_http_basic_auth(self, host, req, realm):
453 user, pw = self.passwd.find_user_password(realm, req.get_full_url())
466 user, pw = self.passwd.find_user_password(realm, req.get_full_url())
454 if pw is not None:
467 if pw is not None:
455 raw = "%s:%s" % (user, pw)
468 raw = "%s:%s" % (user, pw)
456 auth = 'Basic %s' % base64.b64encode(raw).strip()
469 auth = 'Basic %s' % base64.b64encode(raw).strip()
457 if req.headers.get(self.auth_header, None) == auth:
470 if req.headers.get(self.auth_header, None) == auth:
458 return None
471 return None
459 self.auth = auth
472 self.auth = auth
460 req.add_unredirected_header(self.auth_header, auth)
473 req.add_unredirected_header(self.auth_header, auth)
461 return self.parent.open(req)
474 return self.parent.open(req)
462 else:
475 else:
463 return None
476 return None
464
477
465 handlerfuncs = []
478 handlerfuncs = []
466
479
467 def opener(ui, authinfo=None):
480 def opener(ui, authinfo=None):
468 '''
481 '''
469 construct an opener suitable for urllib2
482 construct an opener suitable for urllib2
470 authinfo will be added to the password manager
483 authinfo will be added to the password manager
471 '''
484 '''
472 # experimental config: ui.usehttp2
485 # experimental config: ui.usehttp2
473 if ui.configbool('ui', 'usehttp2', False):
486 if ui.configbool('ui', 'usehttp2', False):
474 handlers = [httpconnectionmod.http2handler(ui, passwordmgr(ui))]
487 handlers = [httpconnectionmod.http2handler(ui, passwordmgr(ui))]
475 else:
488 else:
476 handlers = [httphandler()]
489 handlers = [httphandler()]
477 if has_https:
490 if has_https:
478 handlers.append(httpshandler(ui))
491 handlers.append(httpshandler(ui))
479
492
480 handlers.append(proxyhandler(ui))
493 handlers.append(proxyhandler(ui))
481
494
482 passmgr = passwordmgr(ui)
495 passmgr = passwordmgr(ui)
483 if authinfo is not None:
496 if authinfo is not None:
484 passmgr.add_password(*authinfo)
497 passmgr.add_password(*authinfo)
485 user, passwd = authinfo[2:4]
498 user, passwd = authinfo[2:4]
486 ui.debug('http auth: user %s, password %s\n' %
499 ui.debug('http auth: user %s, password %s\n' %
487 (user, passwd and '*' * len(passwd) or 'not set'))
500 (user, passwd and '*' * len(passwd) or 'not set'))
488
501
489 handlers.extend((httpbasicauthhandler(passmgr),
502 handlers.extend((httpbasicauthhandler(passmgr),
490 httpdigestauthhandler(passmgr)))
503 httpdigestauthhandler(passmgr)))
491 handlers.extend([h(ui, passmgr) for h in handlerfuncs])
504 handlers.extend([h(ui, passmgr) for h in handlerfuncs])
492 opener = urllib2.build_opener(*handlers)
505 opener = urllib2.build_opener(*handlers)
493
506
494 # 1.0 here is the _protocol_ version
507 # 1.0 here is the _protocol_ version
495 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
508 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
496 opener.addheaders.append(('Accept', 'application/mercurial-0.1'))
509 opener.addheaders.append(('Accept', 'application/mercurial-0.1'))
497 return opener
510 return opener
498
511
499 def open(ui, url_, data=None):
512 def open(ui, url_, data=None):
500 u = util.url(url_)
513 u = util.url(url_)
501 if u.scheme:
514 if u.scheme:
502 u.scheme = u.scheme.lower()
515 u.scheme = u.scheme.lower()
503 url_, authinfo = u.authinfo()
516 url_, authinfo = u.authinfo()
504 else:
517 else:
505 path = util.normpath(os.path.abspath(url_))
518 path = util.normpath(os.path.abspath(url_))
506 url_ = 'file://' + urllib.pathname2url(path)
519 url_ = 'file://' + urllib.pathname2url(path)
507 authinfo = None
520 authinfo = None
508 return opener(ui, authinfo).open(url_, data)
521 return opener(ui, authinfo).open(url_, data)
General Comments 0
You need to be logged in to leave comments. Login now