comparison mercurial/wireproto.py @ 37295:45b39c69fae0

wireproto: separate commands tables for version 1 and 2 commands We can't easily reuse existing command handlers for version 2 commands because the response types will be different. e.g. many commands return nodes encoded as hex. Our new wire protocol is binary safe, so we'll wish to encode nodes as binary. We /could/ teach each command handler to look at the protocol handler and change behavior based on the version in use. However, this would make logic a bit unwieldy over time and would make it harder to design a unified protocol handler interface. I think it's better to create a clean break between version 1 and version 2 of commands on the server. What I imagine happening is we will have separate @wireprotocommand functions for each protocol generation. Those functions will parse the request, dispatch to a common function to process it, then generate the response in its own, transport-specific manner. This commit establishes a separate table for tracking version 1 commands from version 2 commands. The HTTP server pieces have been updated to use this new table. Most commands are marked as both version 1 and version 2, so there is little practical impact to this change. A side-effect of this change is we now rely on transport registration in wireprototypes.TRANSPORTS and certain properties of the protocol interface. So a test had to be updated to conform. Differential Revision: https://phab.mercurial-scm.org/D2982
author Gregory Szorc <gregory.szorc@gmail.com>
date Wed, 28 Mar 2018 10:40:41 -0700
parents d5d665f6615a
children 39f7d4ee8bcd
comparison
equal deleted inserted replaced
37294:27527d8cff5c 37295:45b39c69fae0
500 """ 500 """
501 return repo.filtered('served') 501 return repo.filtered('served')
502 502
503 def dispatch(repo, proto, command): 503 def dispatch(repo, proto, command):
504 repo = getdispatchrepo(repo, proto, command) 504 repo = getdispatchrepo(repo, proto, command)
505 func, spec = commands[command] 505
506 transportversion = wireprototypes.TRANSPORTS[proto.name]['version']
507 commandtable = commandsv2 if transportversion == 2 else commands
508 func, spec = commandtable[command]
509
506 args = proto.getargs(spec) 510 args = proto.getargs(spec)
507 return func(repo, proto, *args) 511 return func(repo, proto, *args)
508 512
509 def options(cmd, keys, others): 513 def options(cmd, keys, others):
510 opts = {} 514 opts = {}
677 # available on. For use with @wireprotocommand. 681 # available on. For use with @wireprotocommand.
678 POLICY_ALL = 'all' 682 POLICY_ALL = 'all'
679 POLICY_V1_ONLY = 'v1-only' 683 POLICY_V1_ONLY = 'v1-only'
680 POLICY_V2_ONLY = 'v2-only' 684 POLICY_V2_ONLY = 'v2-only'
681 685
686 # For version 1 transports.
682 commands = commanddict() 687 commands = commanddict()
688
689 # For version 2 transports.
690 commandsv2 = commanddict()
683 691
684 def wireprotocommand(name, args='', transportpolicy=POLICY_ALL, 692 def wireprotocommand(name, args='', transportpolicy=POLICY_ALL,
685 permission='push'): 693 permission='push'):
686 """Decorator to declare a wire protocol command. 694 """Decorator to declare a wire protocol command.
687 695
700 because otherwise commands not declaring their permissions could modify 708 because otherwise commands not declaring their permissions could modify
701 a repository that is supposed to be read-only. 709 a repository that is supposed to be read-only.
702 """ 710 """
703 if transportpolicy == POLICY_ALL: 711 if transportpolicy == POLICY_ALL:
704 transports = set(wireprototypes.TRANSPORTS) 712 transports = set(wireprototypes.TRANSPORTS)
713 transportversions = {1, 2}
705 elif transportpolicy == POLICY_V1_ONLY: 714 elif transportpolicy == POLICY_V1_ONLY:
706 transports = {k for k, v in wireprototypes.TRANSPORTS.items() 715 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
707 if v['version'] == 1} 716 if v['version'] == 1}
717 transportversions = {1}
708 elif transportpolicy == POLICY_V2_ONLY: 718 elif transportpolicy == POLICY_V2_ONLY:
709 transports = {k for k, v in wireprototypes.TRANSPORTS.items() 719 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
710 if v['version'] == 2} 720 if v['version'] == 2}
721 transportversions = {2}
711 else: 722 else:
712 raise error.ProgrammingError('invalid transport policy value: %s' % 723 raise error.ProgrammingError('invalid transport policy value: %s' %
713 transportpolicy) 724 transportpolicy)
714 725
715 # Because SSHv2 is a mirror of SSHv1, we allow "batch" commands through to 726 # Because SSHv2 is a mirror of SSHv1, we allow "batch" commands through to
722 raise error.ProgrammingError('invalid wire protocol permission; ' 733 raise error.ProgrammingError('invalid wire protocol permission; '
723 'got %s; expected "push" or "pull"' % 734 'got %s; expected "push" or "pull"' %
724 permission) 735 permission)
725 736
726 def register(func): 737 def register(func):
727 commands[name] = commandentry(func, args=args, transports=transports, 738 if 1 in transportversions:
728 permission=permission) 739 if name in commands:
740 raise error.ProgrammingError('%s command already registered '
741 'for version 1' % name)
742 commands[name] = commandentry(func, args=args,
743 transports=transports,
744 permission=permission)
745 if 2 in transportversions:
746 if name in commandsv2:
747 raise error.ProgrammingError('%s command already registered '
748 'for version 2' % name)
749 commandsv2[name] = commandentry(func, args=args,
750 transports=transports,
751 permission=permission)
752
729 return func 753 return func
730 return register 754 return register
731 755
732 # TODO define a more appropriate permissions type to use for this. 756 # TODO define a more appropriate permissions type to use for this.
733 @wireprotocommand('batch', 'cmds *', permission='pull', 757 @wireprotocommand('batch', 'cmds *', permission='pull',