##// END OF EJS Templates
fix: updated command list mapping for mercurial 6.5, after client upgrade a new command can be given which is clonebundles_manifest, we marked it explicit as pull command. Also added few more commands, fixes RCCE-111
super-admin -
r5484:aa514264 default
parent child Browse files
Show More
@@ -1,161 +1,167 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 """
21 21 SimpleHG middleware for handling mercurial protocol request
22 22 (push/clone etc.). It's implemented with basic auth function
23 23 """
24 24
25 25 import logging
26 26 import urllib.parse
27 27 import urllib.request
28 28 import urllib.parse
29 29 import urllib.error
30 30
31 31 from rhodecode.lib import utils
32 32 from rhodecode.lib.ext_json import json
33 33 from rhodecode.lib.middleware import simplevcs
34 34 from rhodecode.lib.middleware.utils import get_path_info
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38
39 39 class SimpleHg(simplevcs.SimpleVCS):
40 40
41 41 SCM = 'hg'
42 42
43 43 def _get_repository_name(self, environ):
44 44 """
45 45 Gets repository name out of PATH_INFO header
46 46
47 47 :param environ: environ where PATH_INFO is stored
48 48 """
49 49 repo_name = get_path_info(environ)
50 50 if repo_name and repo_name.startswith('/'):
51 51 # remove only the first leading /
52 52 repo_name = repo_name[1:]
53 53 return repo_name.rstrip('/')
54 54
55 55 _ACTION_MAPPING = {
56 'between': 'pull',
57 'branches': 'pull',
58 'branchmap': 'pull',
59 'capabilities': 'pull',
56 60 'changegroup': 'pull',
57 61 'changegroupsubset': 'pull',
62 'changesetdata': 'pull',
63 'clonebundles': 'pull',
64 'clonebundles_manifest': 'pull',
65 'debugwireargs': 'pull',
66 'filedata': 'pull',
58 67 'getbundle': 'pull',
59 'stream_out': 'pull',
60 'listkeys': 'pull',
61 'between': 'pull',
62 'branchmap': 'pull',
63 'branches': 'pull',
64 'clonebundles': 'pull',
65 'capabilities': 'pull',
66 'debugwireargs': 'pull',
67 68 'heads': 'pull',
68 'lookup': 'pull',
69 69 'hello': 'pull',
70 70 'known': 'pull',
71 'listkeys': 'pull',
72 'lookup': 'pull',
73 'manifestdata': 'pull',
74 'narrow_widen': 'pull',
75 'protocaps': 'pull',
76 'stream_out': 'pull',
71 77
72 78 # largefiles
79 'getlfile': 'pull',
73 80 'putlfile': 'push',
74 'getlfile': 'pull',
75 81 'statlfile': 'pull',
76 82 'lheads': 'pull',
77 83
78 84 # evolve
79 85 'evoext_obshashrange_v1': 'pull',
80 86 'evoext_obshash': 'pull',
81 87 'evoext_obshash1': 'pull',
82 88
83 89 'unbundle': 'push',
84 90 'pushkey': 'push',
85 91 }
86 92
87 93 @classmethod
88 94 def _get_xarg_headers(cls, environ):
89 95 i = 1
90 96 chunks = [] # gather chunks stored in multiple 'hgarg_N'
91 97 while True:
92 98 head = environ.get('HTTP_X_HGARG_{}'.format(i))
93 99 if not head:
94 100 break
95 101 i += 1
96 102 chunks.append(urllib.parse.unquote_plus(head))
97 103 full_arg = ''.join(chunks)
98 104 pref = 'cmds='
99 105 if full_arg.startswith(pref):
100 106 # strip the cmds= header defining our batch commands
101 107 full_arg = full_arg[len(pref):]
102 108 cmds = full_arg.split(';')
103 109 return cmds
104 110
105 111 @classmethod
106 112 def _get_batch_cmd(cls, environ):
107 113 """
108 114 Handle batch command send commands. Those are ';' separated commands
109 115 sent by batch command that server needs to execute. We need to extract
110 116 those, and map them to our ACTION_MAPPING to get all push/pull commands
111 117 specified in the batch
112 118 """
113 119 default = 'push'
114 120 batch_cmds = []
115 121 try:
116 122 cmds = cls._get_xarg_headers(environ)
117 123 for pair in cmds:
118 124 parts = pair.split(' ', 1)
119 125 if len(parts) != 2:
120 126 continue
121 127 # entry should be in a format `key ARGS`
122 128 cmd, args = parts
123 129 action = cls._ACTION_MAPPING.get(cmd, default)
124 130 batch_cmds.append(action)
125 131 except Exception:
126 132 log.exception('Failed to extract batch commands operations')
127 133
128 134 # in case we failed, (e.g malformed data) assume it's PUSH sub-command
129 135 # for safety
130 136 return batch_cmds or [default]
131 137
132 138 def _get_action(self, environ):
133 139 """
134 140 Maps mercurial request commands into a pull or push command.
135 141 In case of unknown/unexpected data, it returns 'push' to be safe.
136 142
137 143 :param environ:
138 144 """
139 145 default = 'push'
140 146 query = urllib.parse.parse_qs(environ['QUERY_STRING'], keep_blank_values=True)
141 147
142 148 if 'cmd' in query:
143 149 cmd = query['cmd'][0]
144 150 if cmd == 'batch':
145 151 cmds = self._get_batch_cmd(environ)
146 152 if 'push' in cmds:
147 153 return 'push'
148 154 else:
149 155 return 'pull'
150 156 return self._ACTION_MAPPING.get(cmd, default)
151 157
152 158 return default
153 159
154 160 def _create_wsgi_app(self, repo_path, repo_name, config):
155 161 return self.scm_app.create_hg_wsgi_app(repo_path, repo_name, config)
156 162
157 163 def _create_config(self, extras, repo_name, scheme='http'):
158 164 config = utils.make_db_config(repo=repo_name)
159 165 config.set('rhodecode', 'RC_SCM_DATA', json.dumps(extras))
160 166
161 167 return config.serialize()
General Comments 0
You need to be logged in to leave comments. Login now