comparison mercurial/httppeer.py @ 40024:86b22a4cfab1

wireprotov2: client support for advertising redirect targets With the server now able to emit a redirect target descriptor, we can start to teach the client to recognize it. This commit implements support for filtering the advertised redirect targets against supported features and for advertising compatible redirect targets as part of command requests. It also adds the minimal boilerplate required to fail when a content redirect is seen. The server doesn't yet do anything with the advertised redirect targets. And the client can't yet follow redirects if it did. But at least we're putting bytes on the wire. Differential Revision: https://phab.mercurial-scm.org/D4776
author Gregory Szorc <gregory.szorc@gmail.com>
date Wed, 26 Sep 2018 15:02:19 -0700
parents f5a05bb48116
children 7e807b8a9e56
comparison
equal deleted inserted replaced
40023:10cf8b116dd8 40024:86b22a4cfab1
506 return self._callstream(cmd, _compressible=True, **args) 506 return self._callstream(cmd, _compressible=True, **args)
507 507
508 def _abort(self, exception): 508 def _abort(self, exception):
509 raise exception 509 raise exception
510 510
511 def sendv2request(ui, opener, requestbuilder, apiurl, permission, requests): 511 def sendv2request(ui, opener, requestbuilder, apiurl, permission, requests,
512 redirect):
512 reactor = wireprotoframing.clientreactor(hasmultiplesend=False, 513 reactor = wireprotoframing.clientreactor(hasmultiplesend=False,
513 buffersends=True) 514 buffersends=True)
514 515
515 handler = wireprotov2peer.clienthandler(ui, reactor) 516 handler = wireprotov2peer.clienthandler(ui, reactor)
516 517
523 524
524 ui.debug('sending %d commands\n' % len(requests)) 525 ui.debug('sending %d commands\n' % len(requests))
525 for command, args, f in requests: 526 for command, args, f in requests:
526 ui.debug('sending command %s: %s\n' % ( 527 ui.debug('sending command %s: %s\n' % (
527 command, stringutil.pprint(args, indent=2))) 528 command, stringutil.pprint(args, indent=2)))
528 assert not list(handler.callcommand(command, args, f)) 529 assert not list(handler.callcommand(command, args, f,
530 redirect=redirect))
529 531
530 # TODO stream this. 532 # TODO stream this.
531 body = b''.join(map(bytes, handler.flushcommands())) 533 body = b''.join(map(bytes, handler.flushcommands()))
532 534
533 # TODO modify user-agent to reflect v2 535 # TODO modify user-agent to reflect v2
565 # will resolve to Future.result. 567 # will resolve to Future.result.
566 return self.result(timeout) 568 return self.result(timeout)
567 569
568 @interfaceutil.implementer(repository.ipeercommandexecutor) 570 @interfaceutil.implementer(repository.ipeercommandexecutor)
569 class httpv2executor(object): 571 class httpv2executor(object):
570 def __init__(self, ui, opener, requestbuilder, apiurl, descriptor): 572 def __init__(self, ui, opener, requestbuilder, apiurl, descriptor,
573 redirect):
571 self._ui = ui 574 self._ui = ui
572 self._opener = opener 575 self._opener = opener
573 self._requestbuilder = requestbuilder 576 self._requestbuilder = requestbuilder
574 self._apiurl = apiurl 577 self._apiurl = apiurl
575 self._descriptor = descriptor 578 self._descriptor = descriptor
579 self._redirect = redirect
576 self._sent = False 580 self._sent = False
577 self._closed = False 581 self._closed = False
578 self._neededpermissions = set() 582 self._neededpermissions = set()
579 self._calls = [] 583 self._calls = []
580 self._futures = weakref.WeakSet() 584 self._futures = weakref.WeakSet()
670 'pull': 'ro', 674 'pull': 'ro',
671 }[permissions.pop()] 675 }[permissions.pop()]
672 676
673 handler, resp = sendv2request( 677 handler, resp = sendv2request(
674 self._ui, self._opener, self._requestbuilder, self._apiurl, 678 self._ui, self._opener, self._requestbuilder, self._apiurl,
675 permission, calls) 679 permission, calls, self._redirect)
676 680
677 # TODO we probably want to validate the HTTP code, media type, etc. 681 # TODO we probably want to validate the HTTP code, media type, etc.
678 682
679 self._responseexecutor = pycompat.futures.ThreadPoolExecutor(1) 683 self._responseexecutor = pycompat.futures.ThreadPoolExecutor(1)
680 self._responsef = self._responseexecutor.submit(self._handleresponse, 684 self._responsef = self._responseexecutor.submit(self._handleresponse,
732 self._apiurl = '%s/%s' % (repourl, apipath) 736 self._apiurl = '%s/%s' % (repourl, apipath)
733 self._opener = opener 737 self._opener = opener
734 self._requestbuilder = requestbuilder 738 self._requestbuilder = requestbuilder
735 self._descriptor = apidescriptor 739 self._descriptor = apidescriptor
736 740
741 self._redirect = wireprotov2peer.supportedredirects(ui, apidescriptor)
742
737 # Start of ipeerconnection. 743 # Start of ipeerconnection.
738 744
739 def url(self): 745 def url(self):
740 return self._url 746 return self._url
741 747
789 with self.commandexecutor() as e: 795 with self.commandexecutor() as e:
790 return e.callcommand(name, args).result() 796 return e.callcommand(name, args).result()
791 797
792 def commandexecutor(self): 798 def commandexecutor(self):
793 return httpv2executor(self.ui, self._opener, self._requestbuilder, 799 return httpv2executor(self.ui, self._opener, self._requestbuilder,
794 self._apiurl, self._descriptor) 800 self._apiurl, self._descriptor, self._redirect)
795 801
796 # Registry of API service names to metadata about peers that handle it. 802 # Registry of API service names to metadata about peers that handle it.
797 # 803 #
798 # The following keys are meaningful: 804 # The following keys are meaningful:
799 # 805 #