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