Mercurial > hg
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: |