comparison mercurial/wireproto.py @ 37541:3e5e37204b32

wireproto: disallow commands handlers for multiple transport versions I think it will be more trouble than it is worth to code version 1 and version 2 command handlers to the same interface. It will feel awkward to shoehorn functionality into e.g. the version 1 protocol handler interface. This would likely constrain the ability for version 2 to evolve. Previous commits introduced a clean separation between command handlers for version 1 and version 2 transports. This commit reinforces that separation by dropping support for having a single command handler service both version 1 and version 2 transports. Differential Revision: https://phab.mercurial-scm.org/D3208
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 09 Apr 2018 11:33:38 -0700
parents 693cb3768943
children 3a2367e6c6f2
comparison
equal deleted inserted replaced
37540:693cb3768943 37541:3e5e37204b32
695 695
696 return True 696 return True
697 697
698 # Constants specifying which transports a wire protocol command should be 698 # Constants specifying which transports a wire protocol command should be
699 # available on. For use with @wireprotocommand. 699 # available on. For use with @wireprotocommand.
700 POLICY_ALL = 'all'
701 POLICY_V1_ONLY = 'v1-only' 700 POLICY_V1_ONLY = 'v1-only'
702 POLICY_V2_ONLY = 'v2-only' 701 POLICY_V2_ONLY = 'v2-only'
703 702
704 # For version 1 transports. 703 # For version 1 transports.
705 commands = commanddict() 704 commands = commanddict()
706 705
707 # For version 2 transports. 706 # For version 2 transports.
708 commandsv2 = commanddict() 707 commandsv2 = commanddict()
709 708
710 def wireprotocommand(name, args='', transportpolicy=POLICY_V1_ONLY, 709 def wireprotocommand(name, args=None, transportpolicy=POLICY_V1_ONLY,
711 permission='push'): 710 permission='push'):
712 """Decorator to declare a wire protocol command. 711 """Decorator to declare a wire protocol command.
713 712
714 ``name`` is the name of the wire protocol command being provided. 713 ``name`` is the name of the wire protocol command being provided.
715 714
727 Can be ``push`` or ``pull``. These roughly map to read-write and read-only, 726 Can be ``push`` or ``pull``. These roughly map to read-write and read-only,
728 respectively. Default is to assume command requires ``push`` permissions 727 respectively. Default is to assume command requires ``push`` permissions
729 because otherwise commands not declaring their permissions could modify 728 because otherwise commands not declaring their permissions could modify
730 a repository that is supposed to be read-only. 729 a repository that is supposed to be read-only.
731 """ 730 """
732 if transportpolicy == POLICY_ALL: 731 if transportpolicy == POLICY_V1_ONLY:
733 transports = set(wireprototypes.TRANSPORTS)
734 transportversions = {1, 2}
735 elif transportpolicy == POLICY_V1_ONLY:
736 transports = {k for k, v in wireprototypes.TRANSPORTS.items() 732 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
737 if v['version'] == 1} 733 if v['version'] == 1}
738 transportversions = {1} 734 transportversion = 1
739 elif transportpolicy == POLICY_V2_ONLY: 735 elif transportpolicy == POLICY_V2_ONLY:
740 transports = {k for k, v in wireprototypes.TRANSPORTS.items() 736 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
741 if v['version'] == 2} 737 if v['version'] == 2}
742 transportversions = {2} 738 transportversion = 2
743 else: 739 else:
744 raise error.ProgrammingError('invalid transport policy value: %s' % 740 raise error.ProgrammingError('invalid transport policy value: %s' %
745 transportpolicy) 741 transportpolicy)
746 742
747 # Because SSHv2 is a mirror of SSHv1, we allow "batch" commands through to 743 # Because SSHv2 is a mirror of SSHv1, we allow "batch" commands through to
753 if permission not in ('push', 'pull'): 749 if permission not in ('push', 'pull'):
754 raise error.ProgrammingError('invalid wire protocol permission; ' 750 raise error.ProgrammingError('invalid wire protocol permission; '
755 'got %s; expected "push" or "pull"' % 751 'got %s; expected "push" or "pull"' %
756 permission) 752 permission)
757 753
758 if 1 in transportversions and not isinstance(args, bytes): 754 if transportversion == 1:
759 raise error.ProgrammingError('arguments for version 1 commands must ' 755 if args is None:
760 'be declared as bytes') 756 args = ''
761 757
762 if isinstance(args, bytes): 758 if not isinstance(args, bytes):
763 dictargs = {arg: b'legacy' for arg in args.split()} 759 raise error.ProgrammingError('arguments for version 1 commands '
764 elif isinstance(args, dict): 760 'must be declared as bytes')
765 dictargs = args 761 elif transportversion == 2:
766 else: 762 if args is None:
767 raise ValueError('args must be bytes or a dict') 763 args = {}
764
765 if not isinstance(args, dict):
766 raise error.ProgrammingError('arguments for version 2 commands '
767 'must be declared as dicts')
768 768
769 def register(func): 769 def register(func):
770 if 1 in transportversions: 770 if transportversion == 1:
771 if name in commands: 771 if name in commands:
772 raise error.ProgrammingError('%s command already registered ' 772 raise error.ProgrammingError('%s command already registered '
773 'for version 1' % name) 773 'for version 1' % name)
774 commands[name] = commandentry(func, args=args, 774 commands[name] = commandentry(func, args=args,
775 transports=transports, 775 transports=transports,
776 permission=permission) 776 permission=permission)
777 if 2 in transportversions: 777 elif transportversion == 2:
778 if name in commandsv2: 778 if name in commandsv2:
779 raise error.ProgrammingError('%s command already registered ' 779 raise error.ProgrammingError('%s command already registered '
780 'for version 2' % name) 780 'for version 2' % name)
781 781
782 commandsv2[name] = commandentry(func, args=dictargs, 782 commandsv2[name] = commandentry(func, args=args,
783 transports=transports, 783 transports=transports,
784 permission=permission) 784 permission=permission)
785 else:
786 raise error.ProgrammingError('unhandled transport version: %d' %
787 transportversion)
785 788
786 return func 789 return func
787 return register 790 return register
788 791
789 # TODO define a more appropriate permissions type to use for this. 792 # TODO define a more appropriate permissions type to use for this.