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