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