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