comparison mercurial/url.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 2a3c0106ded9
children 687b865b95ad
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
23 pycompat, 23 pycompat,
24 sslutil, 24 sslutil,
25 urllibcompat, 25 urllibcompat,
26 util, 26 util,
27 ) 27 )
28 from .utils import ( 28 from .utils import stringutil
29 stringutil,
30 )
31 29
32 httplib = util.httplib 30 httplib = util.httplib
33 stringio = util.stringio 31 stringio = util.stringio
34 urlerr = util.urlerr 32 urlerr = util.urlerr
35 urlreq = util.urlreq 33 urlreq = util.urlreq
34
36 35
37 def escape(s, quote=None): 36 def escape(s, quote=None):
38 '''Replace special characters "&", "<" and ">" to HTML-safe sequences. 37 '''Replace special characters "&", "<" and ">" to HTML-safe sequences.
39 If the optional flag quote is true, the quotation mark character (") 38 If the optional flag quote is true, the quotation mark character (")
40 is also translated. 39 is also translated.
46 s = s.replace(b"<", b"&lt;") 45 s = s.replace(b"<", b"&lt;")
47 s = s.replace(b">", b"&gt;") 46 s = s.replace(b">", b"&gt;")
48 if quote: 47 if quote:
49 s = s.replace(b'"', b"&quot;") 48 s = s.replace(b'"', b"&quot;")
50 return s 49 return s
50
51 51
52 class passwordmgr(object): 52 class passwordmgr(object):
53 def __init__(self, ui, passwddb): 53 def __init__(self, ui, passwddb):
54 self.ui = ui 54 self.ui = ui
55 self.passwddb = passwddb 55 self.passwddb = passwddb
75 self.ui.debug("using auth.%s.* for authentication\n" % group) 75 self.ui.debug("using auth.%s.* for authentication\n" % group)
76 if not user or not passwd: 76 if not user or not passwd:
77 u = util.url(pycompat.bytesurl(authuri)) 77 u = util.url(pycompat.bytesurl(authuri))
78 u.query = None 78 u.query = None
79 if not self.ui.interactive(): 79 if not self.ui.interactive():
80 raise error.Abort(_('http authorization required for %s') % 80 raise error.Abort(
81 util.hidepassword(bytes(u))) 81 _('http authorization required for %s')
82 82 % util.hidepassword(bytes(u))
83 self.ui.write(_("http authorization required for %s\n") % 83 )
84 util.hidepassword(bytes(u))) 84
85 self.ui.write(
86 _("http authorization required for %s\n")
87 % util.hidepassword(bytes(u))
88 )
85 self.ui.write(_("realm: %s\n") % pycompat.bytesurl(realm)) 89 self.ui.write(_("realm: %s\n") % pycompat.bytesurl(realm))
86 if user: 90 if user:
87 self.ui.write(_("user: %s\n") % user) 91 self.ui.write(_("user: %s\n") % user)
88 else: 92 else:
89 user = self.ui.prompt(_("user:"), default=None) 93 user = self.ui.prompt(_("user:"), default=None)
100 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set')) 104 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
101 105
102 def find_stored_password(self, authuri): 106 def find_stored_password(self, authuri):
103 return self.passwddb.find_user_password(None, authuri) 107 return self.passwddb.find_user_password(None, authuri)
104 108
109
105 class proxyhandler(urlreq.proxyhandler): 110 class proxyhandler(urlreq.proxyhandler):
106 def __init__(self, ui): 111 def __init__(self, ui):
107 proxyurl = (ui.config("http_proxy", "host") or 112 proxyurl = ui.config("http_proxy", "host") or encoding.environ.get(
108 encoding.environ.get('http_proxy')) 113 'http_proxy'
114 )
109 # XXX proxyauthinfo = None 115 # XXX proxyauthinfo = None
110 116
111 if proxyurl: 117 if proxyurl:
112 # proxy can be proper url or host[:port] 118 # proxy can be proper url or host[:port]
113 if not (proxyurl.startswith('http:') or 119 if not (
114 proxyurl.startswith('https:')): 120 proxyurl.startswith('http:') or proxyurl.startswith('https:')
121 ):
115 proxyurl = 'http://' + proxyurl + '/' 122 proxyurl = 'http://' + proxyurl + '/'
116 proxy = util.url(proxyurl) 123 proxy = util.url(proxyurl)
117 if not proxy.user: 124 if not proxy.user:
118 proxy.user = ui.config("http_proxy", "user") 125 proxy.user = ui.config("http_proxy", "user")
119 proxy.passwd = ui.config("http_proxy", "passwd") 126 proxy.passwd = ui.config("http_proxy", "passwd")
120 127
121 # see if we should use a proxy for this url 128 # see if we should use a proxy for this url
122 no_list = ["localhost", "127.0.0.1"] 129 no_list = ["localhost", "127.0.0.1"]
123 no_list.extend([p.lower() for 130 no_list.extend(
124 p in ui.configlist("http_proxy", "no")]) 131 [p.lower() for p in ui.configlist("http_proxy", "no")]
125 no_list.extend([p.strip().lower() for 132 )
126 p in encoding.environ.get("no_proxy", '').split(',') 133 no_list.extend(
127 if p.strip()]) 134 [
135 p.strip().lower()
136 for p in encoding.environ.get("no_proxy", '').split(',')
137 if p.strip()
138 ]
139 )
128 # "http_proxy.always" config is for running tests on localhost 140 # "http_proxy.always" config is for running tests on localhost
129 if ui.configbool("http_proxy", "always"): 141 if ui.configbool("http_proxy", "always"):
130 self.no_list = [] 142 self.no_list = []
131 else: 143 else:
132 self.no_list = no_list 144 self.no_list = no_list
151 return None 163 return None
152 if e.startswith('.') and host.endswith(e[1:]): 164 if e.startswith('.') and host.endswith(e[1:]):
153 return None 165 return None
154 166
155 return urlreq.proxyhandler.proxy_open(self, req, proxy, type_) 167 return urlreq.proxyhandler.proxy_open(self, req, proxy, type_)
168
156 169
157 def _gen_sendfile(orgsend): 170 def _gen_sendfile(orgsend):
158 def _sendfile(self, data): 171 def _sendfile(self, data):
159 # send a file 172 # send a file
160 if isinstance(data, httpconnectionmod.httpsendfile): 173 if isinstance(data, httpconnectionmod.httpsendfile):
162 data.seek(0) 175 data.seek(0)
163 for chunk in util.filechunkiter(data): 176 for chunk in util.filechunkiter(data):
164 orgsend(self, chunk) 177 orgsend(self, chunk)
165 else: 178 else:
166 orgsend(self, data) 179 orgsend(self, data)
180
167 return _sendfile 181 return _sendfile
168 182
183
169 has_https = util.safehasattr(urlreq, 'httpshandler') 184 has_https = util.safehasattr(urlreq, 'httpshandler')
185
170 186
171 class httpconnection(keepalive.HTTPConnection): 187 class httpconnection(keepalive.HTTPConnection):
172 # must be able to send big bundle as stream. 188 # must be able to send big bundle as stream.
173 send = _gen_sendfile(keepalive.HTTPConnection.send) 189 send = _gen_sendfile(keepalive.HTTPConnection.send)
174 190
178 if proxyres.will_close: 194 if proxyres.will_close:
179 self.close() 195 self.close()
180 self.proxyres = None 196 self.proxyres = None
181 return proxyres 197 return proxyres
182 return keepalive.HTTPConnection.getresponse(self) 198 return keepalive.HTTPConnection.getresponse(self)
199
183 200
184 # Large parts of this function have their origin from before Python 2.6 201 # Large parts of this function have their origin from before Python 2.6
185 # and could potentially be removed. 202 # and could potentially be removed.
186 def _generic_start_transaction(handler, h, req): 203 def _generic_start_transaction(handler, h, req):
187 tunnel_host = req._tunnel_host 204 tunnel_host = req._tunnel_host
191 new_tunnel = True 208 new_tunnel = True
192 else: 209 else:
193 tunnel_host = urllibcompat.getselector(req) 210 tunnel_host = urllibcompat.getselector(req)
194 new_tunnel = False 211 new_tunnel = False
195 212
196 if new_tunnel or tunnel_host == urllibcompat.getfullurl(req): # has proxy 213 if new_tunnel or tunnel_host == urllibcompat.getfullurl(req): # has proxy
197 u = util.url(pycompat.bytesurl(tunnel_host)) 214 u = util.url(pycompat.bytesurl(tunnel_host))
198 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
199 h.realhostport = ':'.join([u.host, (u.port or '443')]) 216 h.realhostport = ':'.join([u.host, (u.port or '443')])
200 h.headers = req.headers.copy() 217 h.headers = req.headers.copy()
201 h.headers.update(handler.parent.addheaders) 218 h.headers.update(handler.parent.addheaders)
202 return 219 return
203 220
204 h.realhostport = None 221 h.realhostport = None
205 h.headers = None 222 h.headers = None
206 223
224
207 def _generic_proxytunnel(self): 225 def _generic_proxytunnel(self):
208 proxyheaders = dict( 226 proxyheaders = dict(
209 [(x, self.headers[x]) for x in self.headers 227 [
210 if x.lower().startswith(r'proxy-')]) 228 (x, self.headers[x])
229 for x in self.headers
230 if x.lower().startswith(r'proxy-')
231 ]
232 )
211 self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport) 233 self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport)
212 for header in proxyheaders.iteritems(): 234 for header in proxyheaders.iteritems():
213 self.send('%s: %s\r\n' % header) 235 self.send('%s: %s\r\n' % header)
214 self.send('\r\n') 236 self.send('\r\n')
215 237
219 # strict was removed in Python 3.4. 241 # strict was removed in Python 3.4.
220 kwargs = {} 242 kwargs = {}
221 if not pycompat.ispy3: 243 if not pycompat.ispy3:
222 kwargs['strict'] = self.strict 244 kwargs['strict'] = self.strict
223 245
224 res = self.response_class(self.sock, 246 res = self.response_class(self.sock, method=self._method, **kwargs)
225 method=self._method,
226 **kwargs)
227 247
228 while True: 248 while True:
229 version, status, reason = res._read_status() 249 version, status, reason = res._read_status()
230 if status != httplib.CONTINUE: 250 if status != httplib.CONTINUE:
231 break 251 break
283 res.length = None 303 res.length = None
284 else: 304 else:
285 res.length = None 305 res.length = None
286 306
287 # does the body have a fixed length? (of zero) 307 # does the body have a fixed length? (of zero)
288 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or 308 if (
289 100 <= status < 200 or # 1xx codes 309 status == httplib.NO_CONTENT
290 res._method == 'HEAD'): 310 or status == httplib.NOT_MODIFIED
311 or 100 <= status < 200
312 or res._method == 'HEAD' # 1xx codes
313 ):
291 res.length = 0 314 res.length = 0
292 315
293 # if the connection remains open, and we aren't using chunked, and 316 # if the connection remains open, and we aren't using chunked, and
294 # a content-length was not provided, then assume that the connection 317 # a content-length was not provided, then assume that the connection
295 # WILL close. 318 # WILL close.
296 if (not res.will_close and 319 if not res.will_close and not res.chunked and res.length is None:
297 not res.chunked and
298 res.length is None):
299 res.will_close = 1 320 res.will_close = 1
300 321
301 self.proxyres = res 322 self.proxyres = res
302 323
303 return False 324 return False
325
304 326
305 class httphandler(keepalive.HTTPHandler): 327 class httphandler(keepalive.HTTPHandler):
306 def http_open(self, req): 328 def http_open(self, req):
307 return self.do_open(httpconnection, req) 329 return self.do_open(httpconnection, req)
308 330
309 def _start_transaction(self, h, req): 331 def _start_transaction(self, h, req):
310 _generic_start_transaction(self, h, req) 332 _generic_start_transaction(self, h, req)
311 return keepalive.HTTPHandler._start_transaction(self, h, req) 333 return keepalive.HTTPHandler._start_transaction(self, h, req)
334
312 335
313 class logginghttpconnection(keepalive.HTTPConnection): 336 class logginghttpconnection(keepalive.HTTPConnection):
314 def __init__(self, createconn, *args, **kwargs): 337 def __init__(self, createconn, *args, **kwargs):
315 keepalive.HTTPConnection.__init__(self, *args, **kwargs) 338 keepalive.HTTPConnection.__init__(self, *args, **kwargs)
316 self._create_connection = createconn 339 self._create_connection = createconn
317 340
318 if sys.version_info < (2, 7, 7): 341 if sys.version_info < (2, 7, 7):
319 # copied from 2.7.14, since old implementations directly call 342 # copied from 2.7.14, since old implementations directly call
320 # socket.create_connection() 343 # socket.create_connection()
321 def connect(self): 344 def connect(self):
322 self.sock = self._create_connection((self.host, self.port), 345 self.sock = self._create_connection(
323 self.timeout, 346 (self.host, self.port), self.timeout, self.source_address
324 self.source_address) 347 )
325 if self._tunnel_host: 348 if self._tunnel_host:
326 self._tunnel() 349 self._tunnel()
327 350
351
328 class logginghttphandler(httphandler): 352 class logginghttphandler(httphandler):
329 """HTTP handler that logs socket I/O.""" 353 """HTTP handler that logs socket I/O."""
354
330 def __init__(self, logfh, name, observeropts, timeout=None): 355 def __init__(self, logfh, name, observeropts, timeout=None):
331 super(logginghttphandler, self).__init__(timeout=timeout) 356 super(logginghttphandler, self).__init__(timeout=timeout)
332 357
333 self._logfh = logfh 358 self._logfh = logfh
334 self._logname = name 359 self._logname = name
341 return self.do_open(self._makeconnection, req) 366 return self.do_open(self._makeconnection, req)
342 367
343 def _makeconnection(self, *args, **kwargs): 368 def _makeconnection(self, *args, **kwargs):
344 def createconnection(*args, **kwargs): 369 def createconnection(*args, **kwargs):
345 sock = socket.create_connection(*args, **kwargs) 370 sock = socket.create_connection(*args, **kwargs)
346 return util.makeloggingsocket(self._logfh, sock, self._logname, 371 return util.makeloggingsocket(
347 **self._observeropts) 372 self._logfh, sock, self._logname, **self._observeropts
373 )
348 374
349 return logginghttpconnection(createconnection, *args, **kwargs) 375 return logginghttpconnection(createconnection, *args, **kwargs)
350 376
377
351 if has_https: 378 if has_https:
379
352 class httpsconnection(keepalive.HTTPConnection): 380 class httpsconnection(keepalive.HTTPConnection):
353 response_class = keepalive.HTTPResponse 381 response_class = keepalive.HTTPResponse
354 default_port = httplib.HTTPS_PORT 382 default_port = httplib.HTTPS_PORT
355 # must be able to send big bundle as stream. 383 # must be able to send big bundle as stream.
356 send = _gen_sendfile(keepalive.safesend) 384 send = _gen_sendfile(keepalive.safesend)
357 getresponse = keepalive.wrapgetresponse(httplib.HTTPConnection) 385 getresponse = keepalive.wrapgetresponse(httplib.HTTPConnection)
358 386
359 def __init__(self, host, port=None, key_file=None, cert_file=None, 387 def __init__(
360 *args, **kwargs): 388 self,
389 host,
390 port=None,
391 key_file=None,
392 cert_file=None,
393 *args,
394 **kwargs
395 ):
361 keepalive.HTTPConnection.__init__(self, host, port, *args, **kwargs) 396 keepalive.HTTPConnection.__init__(self, host, port, *args, **kwargs)
362 self.key_file = key_file 397 self.key_file = key_file
363 self.cert_file = cert_file 398 self.cert_file = cert_file
364 399
365 def connect(self): 400 def connect(self):
366 self.sock = socket.create_connection((self.host, self.port)) 401 self.sock = socket.create_connection((self.host, self.port))
367 402
368 host = self.host 403 host = self.host
369 if self.realhostport: # use CONNECT proxy 404 if self.realhostport: # use CONNECT proxy
370 _generic_proxytunnel(self) 405 _generic_proxytunnel(self)
371 host = self.realhostport.rsplit(':', 1)[0] 406 host = self.realhostport.rsplit(':', 1)[0]
372 self.sock = sslutil.wrapsocket( 407 self.sock = sslutil.wrapsocket(
373 self.sock, self.key_file, self.cert_file, ui=self.ui, 408 self.sock,
374 serverhostname=host) 409 self.key_file,
410 self.cert_file,
411 ui=self.ui,
412 serverhostname=host,
413 )
375 sslutil.validatesocket(self.sock) 414 sslutil.validatesocket(self.sock)
376 415
377 class httpshandler(keepalive.KeepAliveHandler, urlreq.httpshandler): 416 class httpshandler(keepalive.KeepAliveHandler, urlreq.httpshandler):
378 def __init__(self, ui, timeout=None): 417 def __init__(self, ui, timeout=None):
379 keepalive.KeepAliveHandler.__init__(self, timeout=timeout) 418 keepalive.KeepAliveHandler.__init__(self, timeout=timeout)
380 urlreq.httpshandler.__init__(self) 419 urlreq.httpshandler.__init__(self)
381 self.ui = ui 420 self.ui = ui
382 self.pwmgr = passwordmgr(self.ui, 421 self.pwmgr = passwordmgr(self.ui, self.ui.httppasswordmgrdb)
383 self.ui.httppasswordmgrdb)
384 422
385 def _start_transaction(self, h, req): 423 def _start_transaction(self, h, req):
386 _generic_start_transaction(self, h, req) 424 _generic_start_transaction(self, h, req)
387 return keepalive.KeepAliveHandler._start_transaction(self, h, req) 425 return keepalive.KeepAliveHandler._start_transaction(self, h, req)
388 426
402 440
403 def _makeconnection(self, host, port=None, *args, **kwargs): 441 def _makeconnection(self, host, port=None, *args, **kwargs):
404 keyfile = None 442 keyfile = None
405 certfile = None 443 certfile = None
406 444
407 if len(args) >= 1: # key_file 445 if len(args) >= 1: # key_file
408 keyfile = args[0] 446 keyfile = args[0]
409 if len(args) >= 2: # cert_file 447 if len(args) >= 2: # cert_file
410 certfile = args[1] 448 certfile = args[1]
411 args = args[2:] 449 args = args[2:]
412 450
413 # if the user has specified different key/cert files in 451 # if the user has specified different key/cert files in
414 # hgrc, we prefer these 452 # hgrc, we prefer these
415 if self.auth and 'key' in self.auth and 'cert' in self.auth: 453 if self.auth and 'key' in self.auth and 'cert' in self.auth:
416 keyfile = self.auth['key'] 454 keyfile = self.auth['key']
417 certfile = self.auth['cert'] 455 certfile = self.auth['cert']
418 456
419 conn = httpsconnection(host, port, keyfile, certfile, *args, 457 conn = httpsconnection(
420 **kwargs) 458 host, port, keyfile, certfile, *args, **kwargs
459 )
421 conn.ui = self.ui 460 conn.ui = self.ui
422 return conn 461 return conn
462
423 463
424 class httpdigestauthhandler(urlreq.httpdigestauthhandler): 464 class httpdigestauthhandler(urlreq.httpdigestauthhandler):
425 def __init__(self, *args, **kwargs): 465 def __init__(self, *args, **kwargs):
426 urlreq.httpdigestauthhandler.__init__(self, *args, **kwargs) 466 urlreq.httpdigestauthhandler.__init__(self, *args, **kwargs)
427 self.retried_req = None 467 self.retried_req = None
436 # Reset the retry counter once for each request. 476 # Reset the retry counter once for each request.
437 if req is not self.retried_req: 477 if req is not self.retried_req:
438 self.retried_req = req 478 self.retried_req = req
439 self.retried = 0 479 self.retried = 0
440 return urlreq.httpdigestauthhandler.http_error_auth_reqed( 480 return urlreq.httpdigestauthhandler.http_error_auth_reqed(
441 self, auth_header, host, req, headers) 481 self, auth_header, host, req, headers
482 )
483
442 484
443 class httpbasicauthhandler(urlreq.httpbasicauthhandler): 485 class httpbasicauthhandler(urlreq.httpbasicauthhandler):
444 def __init__(self, *args, **kwargs): 486 def __init__(self, *args, **kwargs):
445 self.auth = None 487 self.auth = None
446 urlreq.httpbasicauthhandler.__init__(self, *args, **kwargs) 488 urlreq.httpbasicauthhandler.__init__(self, *args, **kwargs)
468 # Reset the retry counter once for each request. 510 # Reset the retry counter once for each request.
469 if req is not self.retried_req: 511 if req is not self.retried_req:
470 self.retried_req = req 512 self.retried_req = req
471 self.retried = 0 513 self.retried = 0
472 return urlreq.httpbasicauthhandler.http_error_auth_reqed( 514 return urlreq.httpbasicauthhandler.http_error_auth_reqed(
473 self, auth_header, host, req, headers) 515 self, auth_header, host, req, headers
516 )
474 517
475 def retry_http_basic_auth(self, host, req, realm): 518 def retry_http_basic_auth(self, host, req, realm):
476 user, pw = self.passwd.find_user_password( 519 user, pw = self.passwd.find_user_password(
477 realm, urllibcompat.getfullurl(req)) 520 realm, urllibcompat.getfullurl(req)
521 )
478 if pw is not None: 522 if pw is not None:
479 raw = "%s:%s" % (pycompat.bytesurl(user), pycompat.bytesurl(pw)) 523 raw = "%s:%s" % (pycompat.bytesurl(user), pycompat.bytesurl(pw))
480 auth = r'Basic %s' % pycompat.strurl(base64.b64encode(raw).strip()) 524 auth = r'Basic %s' % pycompat.strurl(base64.b64encode(raw).strip())
481 if req.get_header(self.auth_header, None) == auth: 525 if req.get_header(self.auth_header, None) == auth:
482 return None 526 return None
484 req.add_unredirected_header(self.auth_header, auth) 528 req.add_unredirected_header(self.auth_header, auth)
485 return self.parent.open(req) 529 return self.parent.open(req)
486 else: 530 else:
487 return None 531 return None
488 532
533
489 class cookiehandler(urlreq.basehandler): 534 class cookiehandler(urlreq.basehandler):
490 def __init__(self, ui): 535 def __init__(self, ui):
491 self.cookiejar = None 536 self.cookiejar = None
492 537
493 cookiefile = ui.config('auth', 'cookiefile') 538 cookiefile = ui.config('auth', 'cookiefile')
495 return 540 return
496 541
497 cookiefile = util.expandpath(cookiefile) 542 cookiefile = util.expandpath(cookiefile)
498 try: 543 try:
499 cookiejar = util.cookielib.MozillaCookieJar( 544 cookiejar = util.cookielib.MozillaCookieJar(
500 pycompat.fsdecode(cookiefile)) 545 pycompat.fsdecode(cookiefile)
546 )
501 cookiejar.load() 547 cookiejar.load()
502 self.cookiejar = cookiejar 548 self.cookiejar = cookiejar
503 except util.cookielib.LoadError as e: 549 except util.cookielib.LoadError as e:
504 ui.warn(_('(error loading cookie file %s: %s; continuing without ' 550 ui.warn(
505 'cookies)\n') % (cookiefile, stringutil.forcebytestr(e))) 551 _(
552 '(error loading cookie file %s: %s; continuing without '
553 'cookies)\n'
554 )
555 % (cookiefile, stringutil.forcebytestr(e))
556 )
506 557
507 def http_request(self, request): 558 def http_request(self, request):
508 if self.cookiejar: 559 if self.cookiejar:
509 self.cookiejar.add_cookie_header(request) 560 self.cookiejar.add_cookie_header(request)
510 561
514 if self.cookiejar: 565 if self.cookiejar:
515 self.cookiejar.add_cookie_header(request) 566 self.cookiejar.add_cookie_header(request)
516 567
517 return request 568 return request
518 569
570
519 handlerfuncs = [] 571 handlerfuncs = []
520 572
521 def opener(ui, authinfo=None, useragent=None, loggingfh=None, 573
522 loggingname=b's', loggingopts=None, sendaccept=True): 574 def opener(
575 ui,
576 authinfo=None,
577 useragent=None,
578 loggingfh=None,
579 loggingname=b's',
580 loggingopts=None,
581 sendaccept=True,
582 ):
523 ''' 583 '''
524 construct an opener suitable for urllib2 584 construct an opener suitable for urllib2
525 authinfo will be added to the password manager 585 authinfo will be added to the password manager
526 586
527 The opener can be configured to log socket events if the various 587 The opener can be configured to log socket events if the various
537 ''' 597 '''
538 timeout = ui.configwith(float, 'http', 'timeout') 598 timeout = ui.configwith(float, 'http', 'timeout')
539 handlers = [] 599 handlers = []
540 600
541 if loggingfh: 601 if loggingfh:
542 handlers.append(logginghttphandler(loggingfh, loggingname, 602 handlers.append(
543 loggingopts or {}, timeout=timeout)) 603 logginghttphandler(
604 loggingfh, loggingname, loggingopts or {}, timeout=timeout
605 )
606 )
544 # We don't yet support HTTPS when logging I/O. If we attempt to open 607 # We don't yet support HTTPS when logging I/O. If we attempt to open
545 # an HTTPS URL, we'll likely fail due to unknown protocol. 608 # an HTTPS URL, we'll likely fail due to unknown protocol.
546 609
547 else: 610 else:
548 handlers.append(httphandler(timeout=timeout)) 611 handlers.append(httphandler(timeout=timeout))
555 if authinfo is not None: 618 if authinfo is not None:
556 realm, uris, user, passwd = authinfo 619 realm, uris, user, passwd = authinfo
557 saveduser, savedpass = passmgr.find_stored_password(uris[0]) 620 saveduser, savedpass = passmgr.find_stored_password(uris[0])
558 if user != saveduser or passwd: 621 if user != saveduser or passwd:
559 passmgr.add_password(realm, uris, user, passwd) 622 passmgr.add_password(realm, uris, user, passwd)
560 ui.debug('http auth: user %s, password %s\n' % 623 ui.debug(
561 (user, passwd and '*' * len(passwd) or 'not set')) 624 'http auth: user %s, password %s\n'
562 625 % (user, passwd and '*' * len(passwd) or 'not set')
563 handlers.extend((httpbasicauthhandler(passmgr), 626 )
564 httpdigestauthhandler(passmgr))) 627
628 handlers.extend(
629 (httpbasicauthhandler(passmgr), httpdigestauthhandler(passmgr))
630 )
565 handlers.extend([h(ui, passmgr) for h in handlerfuncs]) 631 handlers.extend([h(ui, passmgr) for h in handlerfuncs])
566 handlers.append(cookiehandler(ui)) 632 handlers.append(cookiehandler(ui))
567 opener = urlreq.buildopener(*handlers) 633 opener = urlreq.buildopener(*handlers)
568 634
569 # keepalive.py's handlers will populate these attributes if they exist. 635 # keepalive.py's handlers will populate these attributes if they exist.
599 if sendaccept: 665 if sendaccept:
600 opener.addheaders.append((r'Accept', r'application/mercurial-0.1')) 666 opener.addheaders.append((r'Accept', r'application/mercurial-0.1'))
601 667
602 return opener 668 return opener
603 669
670
604 def open(ui, url_, data=None, sendaccept=True): 671 def open(ui, url_, data=None, sendaccept=True):
605 u = util.url(url_) 672 u = util.url(url_)
606 if u.scheme: 673 if u.scheme:
607 u.scheme = u.scheme.lower() 674 u.scheme = u.scheme.lower()
608 url_, authinfo = u.authinfo() 675 url_, authinfo = u.authinfo()
609 else: 676 else:
610 path = util.normpath(os.path.abspath(url_)) 677 path = util.normpath(os.path.abspath(url_))
611 url_ = 'file://' + pycompat.bytesurl(urlreq.pathname2url(path)) 678 url_ = 'file://' + pycompat.bytesurl(urlreq.pathname2url(path))
612 authinfo = None 679 authinfo = None
613 return opener(ui, authinfo, 680 return opener(ui, authinfo, sendaccept=sendaccept).open(
614 sendaccept=sendaccept).open(pycompat.strurl(url_), 681 pycompat.strurl(url_), data
615 data) 682 )
683
616 684
617 def wrapresponse(resp): 685 def wrapresponse(resp):
618 """Wrap a response object with common error handlers. 686 """Wrap a response object with common error handlers.
619 687
620 This ensures that any I/O from any consumer raises the appropriate 688 This ensures that any I/O from any consumer raises the appropriate
629 except httplib.IncompleteRead as e: 697 except httplib.IncompleteRead as e:
630 # e.expected is an integer if length known or None otherwise. 698 # e.expected is an integer if length known or None otherwise.
631 if e.expected: 699 if e.expected:
632 got = len(e.partial) 700 got = len(e.partial)
633 total = e.expected + got 701 total = e.expected + got
634 msg = _('HTTP request error (incomplete response; ' 702 msg = _(
635 'expected %d bytes got %d)') % (total, got) 703 'HTTP request error (incomplete response; '
704 'expected %d bytes got %d)'
705 ) % (total, got)
636 else: 706 else:
637 msg = _('HTTP request error (incomplete response)') 707 msg = _('HTTP request error (incomplete response)')
638 708
639 raise error.PeerTransportError( 709 raise error.PeerTransportError(
640 msg, 710 msg,
641 hint=_('this may be an intermittent network failure; ' 711 hint=_(
642 'if the error persists, consider contacting the ' 712 'this may be an intermittent network failure; '
643 'network or server operator')) 713 'if the error persists, consider contacting the '
714 'network or server operator'
715 ),
716 )
644 except httplib.HTTPException as e: 717 except httplib.HTTPException as e:
645 raise error.PeerTransportError( 718 raise error.PeerTransportError(
646 _('HTTP request error (%s)') % e, 719 _('HTTP request error (%s)') % e,
647 hint=_('this may be an intermittent network failure; ' 720 hint=_(
648 'if the error persists, consider contacting the ' 721 'this may be an intermittent network failure; '
649 'network or server operator')) 722 'if the error persists, consider contacting the '
723 'network or server operator'
724 ),
725 )
650 726
651 resp.__class__ = readerproxy 727 resp.__class__ = readerproxy