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