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