comparison mercurial/url.py @ 37011:02221d6fb041

url: add HTTP handler that uses a proxied socket Now that we have a socket proxy that can log I/O, we need to teach mechanisms that open URLs how to use it. We invent a custom HTTP handler class that knows how to proxy sockets as soon as they are opened. We teach the high-level opener() to accept logging arguments so a logging HTTP handler can be constructed. We don't yet support intercepting HTTPS sockets because I don't want to go down that rabbit hole. For the record, the urllib API is crazy and it took way too long to figure out at what levels I needed to plug in to modify the socket. # no-check-commit because we must name http_open that way Differential Revision: https://phab.mercurial-scm.org/D2722
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 12 Mar 2018 15:43:36 -0700
parents 8381126bf43c
children a708e1e4d7a8
comparison
equal deleted inserted replaced
37010:8453699a1f21 37011:02221d6fb041
293 return self.do_open(httpconnection, req) 293 return self.do_open(httpconnection, req)
294 294
295 def _start_transaction(self, h, req): 295 def _start_transaction(self, h, req):
296 _generic_start_transaction(self, h, req) 296 _generic_start_transaction(self, h, req)
297 return keepalive.HTTPHandler._start_transaction(self, h, req) 297 return keepalive.HTTPHandler._start_transaction(self, h, req)
298
299 class logginghttpconnection(keepalive.HTTPConnection):
300 def __init__(self, createconn, *args, **kwargs):
301 keepalive.HTTPConnection.__init__(self, *args, **kwargs)
302 self._create_connection = createconn
303
304 class logginghttphandler(httphandler):
305 """HTTP handler that logs socket I/O."""
306 def __init__(self, logfh, name, observeropts):
307 super(logginghttphandler, self).__init__()
308
309 self._logfh = logfh
310 self._logname = name
311 self._observeropts = observeropts
312
313 # do_open() calls the passed class to instantiate an HTTPConnection. We
314 # pass in a callable method that creates a custom HTTPConnection instance
315 # whose callback to create the socket knows how to proxy the socket.
316 def http_open(self, req):
317 return self.do_open(self._makeconnection, req)
318
319 def _makeconnection(self, *args, **kwargs):
320 def createconnection(*args, **kwargs):
321 sock = socket.create_connection(*args, **kwargs)
322 return util.makeloggingsocket(self._logfh, sock, self._logname,
323 **self._observeropts)
324
325 return logginghttpconnection(createconnection, *args, **kwargs)
298 326
299 if has_https: 327 if has_https:
300 class httpsconnection(httplib.HTTPConnection): 328 class httpsconnection(httplib.HTTPConnection):
301 response_class = keepalive.HTTPResponse 329 response_class = keepalive.HTTPResponse
302 default_port = httplib.HTTPS_PORT 330 default_port = httplib.HTTPS_PORT
463 491
464 return request 492 return request
465 493
466 handlerfuncs = [] 494 handlerfuncs = []
467 495
468 def opener(ui, authinfo=None, useragent=None): 496 def opener(ui, authinfo=None, useragent=None, loggingfh=None,
497 loggingname=b's', loggingopts=None):
469 ''' 498 '''
470 construct an opener suitable for urllib2 499 construct an opener suitable for urllib2
471 authinfo will be added to the password manager 500 authinfo will be added to the password manager
501
502 The opener can be configured to log socket events if the various
503 ``logging*`` arguments are specified.
504
505 ``loggingfh`` denotes a file object to log events to.
506 ``loggingname`` denotes the name of the to print when logging.
507 ``loggingopts`` is a dict of keyword arguments to pass to the constructed
508 ``util.socketobserver`` instance.
472 ''' 509 '''
473 handlers = [httphandler()] 510 handlers = []
474 if has_https: 511
475 handlers.append(httpshandler(ui)) 512 if loggingfh:
513 handlers.append(logginghttphandler(loggingfh, loggingname,
514 loggingopts or {}))
515 # We don't yet support HTTPS when logging I/O. If we attempt to open
516 # an HTTPS URL, we'll likely fail due to unknown protocol.
517
518 else:
519 handlers.append(httphandler())
520 if has_https:
521 handlers.append(httpshandler(ui))
476 522
477 handlers.append(proxyhandler(ui)) 523 handlers.append(proxyhandler(ui))
478 524
479 passmgr = passwordmgr(ui, ui.httppasswordmgrdb) 525 passmgr = passwordmgr(ui, ui.httppasswordmgrdb)
480 if authinfo is not None: 526 if authinfo is not None: