comparison mercurial/wireprotov1server.py @ 43077:687b865b95ad

formatting: byteify all mercurial/ and hgext/ string literals Done with python3.7 contrib/byteify-strings.py -i $(hg files 'set:mercurial/**.py - mercurial/thirdparty/** + hgext/**.py - hgext/fsmonitor/pywatchman/** - mercurial/__init__.py') black -l 80 -t py33 -S $(hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**" - hgext/fsmonitor/pywatchman/**') # skip-blame mass-reformatting only Differential Revision: https://phab.mercurial-scm.org/D6972
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:48:39 -0400
parents 2372284d9457
children c59eb1560c44
comparison
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
36 ) 36 )
37 37
38 urlerr = util.urlerr 38 urlerr = util.urlerr
39 urlreq = util.urlreq 39 urlreq = util.urlreq
40 40
41 bundle2requiredmain = _('incompatible Mercurial client; bundle2 required') 41 bundle2requiredmain = _(b'incompatible Mercurial client; bundle2 required')
42 bundle2requiredhint = _( 42 bundle2requiredhint = _(
43 'see https://www.mercurial-scm.org/wiki/' 'IncompatibleClient' 43 b'see https://www.mercurial-scm.org/wiki/' b'IncompatibleClient'
44 ) 44 )
45 bundle2required = '%s\n(%s)\n' % (bundle2requiredmain, bundle2requiredhint) 45 bundle2required = b'%s\n(%s)\n' % (bundle2requiredmain, bundle2requiredhint)
46 46
47 47
48 def clientcompressionsupport(proto): 48 def clientcompressionsupport(proto):
49 """Returns a list of compression methods supported by the client. 49 """Returns a list of compression methods supported by the client.
50 50
51 Returns a list of the compression methods supported by the client 51 Returns a list of the compression methods supported by the client
52 according to the protocol capabilities. If no such capability has 52 according to the protocol capabilities. If no such capability has
53 been announced, fallback to the default of zlib and uncompressed. 53 been announced, fallback to the default of zlib and uncompressed.
54 """ 54 """
55 for cap in proto.getprotocaps(): 55 for cap in proto.getprotocaps():
56 if cap.startswith('comp='): 56 if cap.startswith(b'comp='):
57 return cap[5:].split(',') 57 return cap[5:].split(b',')
58 return ['zlib', 'none'] 58 return [b'zlib', b'none']
59 59
60 60
61 # wire protocol command can either return a string or one of these classes. 61 # wire protocol command can either return a string or one of these classes.
62 62
63 63
66 66
67 The intent of this function is to serve as a monkeypatch point for 67 The intent of this function is to serve as a monkeypatch point for
68 extensions that need commands to operate on different repo views under 68 extensions that need commands to operate on different repo views under
69 specialized circumstances. 69 specialized circumstances.
70 """ 70 """
71 viewconfig = repo.ui.config('server', 'view') 71 viewconfig = repo.ui.config(b'server', b'view')
72 return repo.filtered(viewconfig) 72 return repo.filtered(viewconfig)
73 73
74 74
75 def dispatch(repo, proto, command): 75 def dispatch(repo, proto, command):
76 repo = getdispatchrepo(repo, proto, command) 76 repo = getdispatchrepo(repo, proto, command)
87 if k in others: 87 if k in others:
88 opts[k] = others[k] 88 opts[k] = others[k]
89 del others[k] 89 del others[k]
90 if others: 90 if others:
91 procutil.stderr.write( 91 procutil.stderr.write(
92 "warning: %s ignored unexpected arguments %s\n" 92 b"warning: %s ignored unexpected arguments %s\n"
93 % (cmd, ",".join(others)) 93 % (cmd, b",".join(others))
94 ) 94 )
95 return opts 95 return opts
96 96
97 97
98 def bundle1allowed(repo, action): 98 def bundle1allowed(repo, action):
104 2. server.bundle1.<action> 104 2. server.bundle1.<action>
105 3. server.bundle1gd (if generaldelta active) 105 3. server.bundle1gd (if generaldelta active)
106 4. server.bundle1 106 4. server.bundle1
107 """ 107 """
108 ui = repo.ui 108 ui = repo.ui
109 gd = 'generaldelta' in repo.requirements 109 gd = b'generaldelta' in repo.requirements
110 110
111 if gd: 111 if gd:
112 v = ui.configbool('server', 'bundle1gd.%s' % action) 112 v = ui.configbool(b'server', b'bundle1gd.%s' % action)
113 if v is not None: 113 if v is not None:
114 return v 114 return v
115 115
116 v = ui.configbool('server', 'bundle1.%s' % action) 116 v = ui.configbool(b'server', b'bundle1.%s' % action)
117 if v is not None: 117 if v is not None:
118 return v 118 return v
119 119
120 if gd: 120 if gd:
121 v = ui.configbool('server', 'bundle1gd') 121 v = ui.configbool(b'server', b'bundle1gd')
122 if v is not None: 122 if v is not None:
123 return v 123 return v
124 124
125 return ui.configbool('server', 'bundle1') 125 return ui.configbool(b'server', b'bundle1')
126 126
127 127
128 commands = wireprototypes.commanddict() 128 commands = wireprototypes.commanddict()
129 129
130 130
131 def wireprotocommand(name, args=None, permission='push'): 131 def wireprotocommand(name, args=None, permission=b'push'):
132 """Decorator to declare a wire protocol command. 132 """Decorator to declare a wire protocol command.
133 133
134 ``name`` is the name of the wire protocol command being provided. 134 ``name`` is the name of the wire protocol command being provided.
135 135
136 ``args`` defines the named arguments accepted by the command. It is 136 ``args`` defines the named arguments accepted by the command. It is
142 respectively. Default is to assume command requires ``push`` permissions 142 respectively. Default is to assume command requires ``push`` permissions
143 because otherwise commands not declaring their permissions could modify 143 because otherwise commands not declaring their permissions could modify
144 a repository that is supposed to be read-only. 144 a repository that is supposed to be read-only.
145 """ 145 """
146 transports = { 146 transports = {
147 k for k, v in wireprototypes.TRANSPORTS.items() if v['version'] == 1 147 k for k, v in wireprototypes.TRANSPORTS.items() if v[b'version'] == 1
148 } 148 }
149 149
150 # Because SSHv2 is a mirror of SSHv1, we allow "batch" commands through to 150 # Because SSHv2 is a mirror of SSHv1, we allow "batch" commands through to
151 # SSHv2. 151 # SSHv2.
152 # TODO undo this hack when SSH is using the unified frame protocol. 152 # TODO undo this hack when SSH is using the unified frame protocol.
153 if name == b'batch': 153 if name == b'batch':
154 transports.add(wireprototypes.SSHV2) 154 transports.add(wireprototypes.SSHV2)
155 155
156 if permission not in ('push', 'pull'): 156 if permission not in (b'push', b'pull'):
157 raise error.ProgrammingError( 157 raise error.ProgrammingError(
158 'invalid wire protocol permission; ' 158 b'invalid wire protocol permission; '
159 'got %s; expected "push" or "pull"' % permission 159 b'got %s; expected "push" or "pull"' % permission
160 ) 160 )
161 161
162 if args is None: 162 if args is None:
163 args = '' 163 args = b''
164 164
165 if not isinstance(args, bytes): 165 if not isinstance(args, bytes):
166 raise error.ProgrammingError( 166 raise error.ProgrammingError(
167 'arguments for version 1 commands ' 'must be declared as bytes' 167 b'arguments for version 1 commands ' b'must be declared as bytes'
168 ) 168 )
169 169
170 def register(func): 170 def register(func):
171 if name in commands: 171 if name in commands:
172 raise error.ProgrammingError( 172 raise error.ProgrammingError(
173 '%s command already registered ' 'for version 1' % name 173 b'%s command already registered ' b'for version 1' % name
174 ) 174 )
175 commands[name] = wireprototypes.commandentry( 175 commands[name] = wireprototypes.commandentry(
176 func, args=args, transports=transports, permission=permission 176 func, args=args, transports=transports, permission=permission
177 ) 177 )
178 178
180 180
181 return register 181 return register
182 182
183 183
184 # TODO define a more appropriate permissions type to use for this. 184 # TODO define a more appropriate permissions type to use for this.
185 @wireprotocommand('batch', 'cmds *', permission='pull') 185 @wireprotocommand(b'batch', b'cmds *', permission=b'pull')
186 def batch(repo, proto, cmds, others): 186 def batch(repo, proto, cmds, others):
187 unescapearg = wireprototypes.unescapebatcharg 187 unescapearg = wireprototypes.unescapebatcharg
188 res = [] 188 res = []
189 for pair in cmds.split(';'): 189 for pair in cmds.split(b';'):
190 op, args = pair.split(' ', 1) 190 op, args = pair.split(b' ', 1)
191 vals = {} 191 vals = {}
192 for a in args.split(','): 192 for a in args.split(b','):
193 if a: 193 if a:
194 n, v = a.split('=') 194 n, v = a.split(b'=')
195 vals[unescapearg(n)] = unescapearg(v) 195 vals[unescapearg(n)] = unescapearg(v)
196 func, spec = commands[op] 196 func, spec = commands[op]
197 197
198 # Validate that client has permissions to perform this command. 198 # Validate that client has permissions to perform this command.
199 perm = commands[op].permission 199 perm = commands[op].permission
200 assert perm in ('push', 'pull') 200 assert perm in (b'push', b'pull')
201 proto.checkperm(perm) 201 proto.checkperm(perm)
202 202
203 if spec: 203 if spec:
204 keys = spec.split() 204 keys = spec.split()
205 data = {} 205 data = {}
206 for k in keys: 206 for k in keys:
207 if k == '*': 207 if k == b'*':
208 star = {} 208 star = {}
209 for key in vals.keys(): 209 for key in vals.keys():
210 if key not in keys: 210 if key not in keys:
211 star[key] = vals[key] 211 star[key] = vals[key]
212 data['*'] = star 212 data[b'*'] = star
213 else: 213 else:
214 data[k] = vals[k] 214 data[k] = vals[k]
215 result = func(repo, proto, *[data[k] for k in keys]) 215 result = func(repo, proto, *[data[k] for k in keys])
216 else: 216 else:
217 result = func(repo, proto) 217 result = func(repo, proto)
223 assert isinstance(result, (wireprototypes.bytesresponse, bytes)) 223 assert isinstance(result, (wireprototypes.bytesresponse, bytes))
224 if isinstance(result, wireprototypes.bytesresponse): 224 if isinstance(result, wireprototypes.bytesresponse):
225 result = result.data 225 result = result.data
226 res.append(wireprototypes.escapebatcharg(result)) 226 res.append(wireprototypes.escapebatcharg(result))
227 227
228 return wireprototypes.bytesresponse(';'.join(res)) 228 return wireprototypes.bytesresponse(b';'.join(res))
229 229
230 230
231 @wireprotocommand('between', 'pairs', permission='pull') 231 @wireprotocommand(b'between', b'pairs', permission=b'pull')
232 def between(repo, proto, pairs): 232 def between(repo, proto, pairs):
233 pairs = [wireprototypes.decodelist(p, '-') for p in pairs.split(" ")] 233 pairs = [wireprototypes.decodelist(p, b'-') for p in pairs.split(b" ")]
234 r = [] 234 r = []
235 for b in repo.between(pairs): 235 for b in repo.between(pairs):
236 r.append(wireprototypes.encodelist(b) + "\n") 236 r.append(wireprototypes.encodelist(b) + b"\n")
237 237
238 return wireprototypes.bytesresponse(''.join(r)) 238 return wireprototypes.bytesresponse(b''.join(r))
239 239
240 240
241 @wireprotocommand('branchmap', permission='pull') 241 @wireprotocommand(b'branchmap', permission=b'pull')
242 def branchmap(repo, proto): 242 def branchmap(repo, proto):
243 branchmap = repo.branchmap() 243 branchmap = repo.branchmap()
244 heads = [] 244 heads = []
245 for branch, nodes in branchmap.iteritems(): 245 for branch, nodes in branchmap.iteritems():
246 branchname = urlreq.quote(encoding.fromlocal(branch)) 246 branchname = urlreq.quote(encoding.fromlocal(branch))
247 branchnodes = wireprototypes.encodelist(nodes) 247 branchnodes = wireprototypes.encodelist(nodes)
248 heads.append('%s %s' % (branchname, branchnodes)) 248 heads.append(b'%s %s' % (branchname, branchnodes))
249 249
250 return wireprototypes.bytesresponse('\n'.join(heads)) 250 return wireprototypes.bytesresponse(b'\n'.join(heads))
251 251
252 252
253 @wireprotocommand('branches', 'nodes', permission='pull') 253 @wireprotocommand(b'branches', b'nodes', permission=b'pull')
254 def branches(repo, proto, nodes): 254 def branches(repo, proto, nodes):
255 nodes = wireprototypes.decodelist(nodes) 255 nodes = wireprototypes.decodelist(nodes)
256 r = [] 256 r = []
257 for b in repo.branches(nodes): 257 for b in repo.branches(nodes):
258 r.append(wireprototypes.encodelist(b) + "\n") 258 r.append(wireprototypes.encodelist(b) + b"\n")
259 259
260 return wireprototypes.bytesresponse(''.join(r)) 260 return wireprototypes.bytesresponse(b''.join(r))
261 261
262 262
263 @wireprotocommand('clonebundles', '', permission='pull') 263 @wireprotocommand(b'clonebundles', b'', permission=b'pull')
264 def clonebundles(repo, proto): 264 def clonebundles(repo, proto):
265 """Server command for returning info for available bundles to seed clones. 265 """Server command for returning info for available bundles to seed clones.
266 266
267 Clients will parse this response and determine what bundle to fetch. 267 Clients will parse this response and determine what bundle to fetch.
268 268
269 Extensions may wrap this command to filter or dynamically emit data 269 Extensions may wrap this command to filter or dynamically emit data
270 depending on the request. e.g. you could advertise URLs for the closest 270 depending on the request. e.g. you could advertise URLs for the closest
271 data center given the client's IP address. 271 data center given the client's IP address.
272 """ 272 """
273 return wireprototypes.bytesresponse( 273 return wireprototypes.bytesresponse(
274 repo.vfs.tryread('clonebundles.manifest') 274 repo.vfs.tryread(b'clonebundles.manifest')
275 ) 275 )
276 276
277 277
278 wireprotocaps = [ 278 wireprotocaps = [
279 'lookup', 279 b'lookup',
280 'branchmap', 280 b'branchmap',
281 'pushkey', 281 b'pushkey',
282 'known', 282 b'known',
283 'getbundle', 283 b'getbundle',
284 'unbundlehash', 284 b'unbundlehash',
285 ] 285 ]
286 286
287 287
288 def _capabilities(repo, proto): 288 def _capabilities(repo, proto):
289 """return a list of capabilities for a repo 289 """return a list of capabilities for a repo
298 # copy to prevent modification of the global list 298 # copy to prevent modification of the global list
299 caps = list(wireprotocaps) 299 caps = list(wireprotocaps)
300 300
301 # Command of same name as capability isn't exposed to version 1 of 301 # Command of same name as capability isn't exposed to version 1 of
302 # transports. So conditionally add it. 302 # transports. So conditionally add it.
303 if commands.commandavailable('changegroupsubset', proto): 303 if commands.commandavailable(b'changegroupsubset', proto):
304 caps.append('changegroupsubset') 304 caps.append(b'changegroupsubset')
305 305
306 if streamclone.allowservergeneration(repo): 306 if streamclone.allowservergeneration(repo):
307 if repo.ui.configbool('server', 'preferuncompressed'): 307 if repo.ui.configbool(b'server', b'preferuncompressed'):
308 caps.append('stream-preferred') 308 caps.append(b'stream-preferred')
309 requiredformats = repo.requirements & repo.supportedformats 309 requiredformats = repo.requirements & repo.supportedformats
310 # if our local revlogs are just revlogv1, add 'stream' cap 310 # if our local revlogs are just revlogv1, add 'stream' cap
311 if not requiredformats - {'revlogv1'}: 311 if not requiredformats - {b'revlogv1'}:
312 caps.append('stream') 312 caps.append(b'stream')
313 # otherwise, add 'streamreqs' detailing our local revlog format 313 # otherwise, add 'streamreqs' detailing our local revlog format
314 else: 314 else:
315 caps.append('streamreqs=%s' % ','.join(sorted(requiredformats))) 315 caps.append(b'streamreqs=%s' % b','.join(sorted(requiredformats)))
316 if repo.ui.configbool('experimental', 'bundle2-advertise'): 316 if repo.ui.configbool(b'experimental', b'bundle2-advertise'):
317 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role='server')) 317 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role=b'server'))
318 caps.append('bundle2=' + urlreq.quote(capsblob)) 318 caps.append(b'bundle2=' + urlreq.quote(capsblob))
319 caps.append('unbundle=%s' % ','.join(bundle2.bundlepriority)) 319 caps.append(b'unbundle=%s' % b','.join(bundle2.bundlepriority))
320 320
321 if repo.ui.configbool('experimental', 'narrow'): 321 if repo.ui.configbool(b'experimental', b'narrow'):
322 caps.append(wireprototypes.NARROWCAP) 322 caps.append(wireprototypes.NARROWCAP)
323 if repo.ui.configbool('experimental', 'narrowservebrokenellipses'): 323 if repo.ui.configbool(b'experimental', b'narrowservebrokenellipses'):
324 caps.append(wireprototypes.ELLIPSESCAP) 324 caps.append(wireprototypes.ELLIPSESCAP)
325 325
326 return proto.addcapabilities(repo, caps) 326 return proto.addcapabilities(repo, caps)
327 327
328 328
329 # If you are writing an extension and consider wrapping this function. Wrap 329 # If you are writing an extension and consider wrapping this function. Wrap
330 # `_capabilities` instead. 330 # `_capabilities` instead.
331 @wireprotocommand('capabilities', permission='pull') 331 @wireprotocommand(b'capabilities', permission=b'pull')
332 def capabilities(repo, proto): 332 def capabilities(repo, proto):
333 caps = _capabilities(repo, proto) 333 caps = _capabilities(repo, proto)
334 return wireprototypes.bytesresponse(' '.join(sorted(caps))) 334 return wireprototypes.bytesresponse(b' '.join(sorted(caps)))
335 335
336 336
337 @wireprotocommand('changegroup', 'roots', permission='pull') 337 @wireprotocommand(b'changegroup', b'roots', permission=b'pull')
338 def changegroup(repo, proto, roots): 338 def changegroup(repo, proto, roots):
339 nodes = wireprototypes.decodelist(roots) 339 nodes = wireprototypes.decodelist(roots)
340 outgoing = discovery.outgoing( 340 outgoing = discovery.outgoing(
341 repo, missingroots=nodes, missingheads=repo.heads() 341 repo, missingroots=nodes, missingheads=repo.heads()
342 ) 342 )
343 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve') 343 cg = changegroupmod.makechangegroup(repo, outgoing, b'01', b'serve')
344 gen = iter(lambda: cg.read(32768), '') 344 gen = iter(lambda: cg.read(32768), b'')
345 return wireprototypes.streamres(gen=gen) 345 return wireprototypes.streamres(gen=gen)
346 346
347 347
348 @wireprotocommand('changegroupsubset', 'bases heads', permission='pull') 348 @wireprotocommand(b'changegroupsubset', b'bases heads', permission=b'pull')
349 def changegroupsubset(repo, proto, bases, heads): 349 def changegroupsubset(repo, proto, bases, heads):
350 bases = wireprototypes.decodelist(bases) 350 bases = wireprototypes.decodelist(bases)
351 heads = wireprototypes.decodelist(heads) 351 heads = wireprototypes.decodelist(heads)
352 outgoing = discovery.outgoing(repo, missingroots=bases, missingheads=heads) 352 outgoing = discovery.outgoing(repo, missingroots=bases, missingheads=heads)
353 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve') 353 cg = changegroupmod.makechangegroup(repo, outgoing, b'01', b'serve')
354 gen = iter(lambda: cg.read(32768), '') 354 gen = iter(lambda: cg.read(32768), b'')
355 return wireprototypes.streamres(gen=gen) 355 return wireprototypes.streamres(gen=gen)
356 356
357 357
358 @wireprotocommand('debugwireargs', 'one two *', permission='pull') 358 @wireprotocommand(b'debugwireargs', b'one two *', permission=b'pull')
359 def debugwireargs(repo, proto, one, two, others): 359 def debugwireargs(repo, proto, one, two, others):
360 # only accept optional args from the known set 360 # only accept optional args from the known set
361 opts = options('debugwireargs', ['three', 'four'], others) 361 opts = options(b'debugwireargs', [b'three', b'four'], others)
362 return wireprototypes.bytesresponse( 362 return wireprototypes.bytesresponse(
363 repo.debugwireargs(one, two, **pycompat.strkwargs(opts)) 363 repo.debugwireargs(one, two, **pycompat.strkwargs(opts))
364 ) 364 )
365 365
366 366
379 E.g. do not send a bundle of all changes if the client wants only 379 E.g. do not send a bundle of all changes if the client wants only
380 one specific branch of many. 380 one specific branch of many.
381 """ 381 """
382 382
383 def decodehexstring(s): 383 def decodehexstring(s):
384 return {binascii.unhexlify(h) for h in s.split(';')} 384 return {binascii.unhexlify(h) for h in s.split(b';')}
385 385
386 manifest = repo.vfs.tryread('pullbundles.manifest') 386 manifest = repo.vfs.tryread(b'pullbundles.manifest')
387 if not manifest: 387 if not manifest:
388 return None 388 return None
389 res = exchange.parseclonebundlesmanifest(repo, manifest) 389 res = exchange.parseclonebundlesmanifest(repo, manifest)
390 res = exchange.filterclonebundleentries(repo, res) 390 res = exchange.filterclonebundleentries(repo, res)
391 if not res: 391 if not res:
393 cl = repo.changelog 393 cl = repo.changelog
394 heads_anc = cl.ancestors([cl.rev(rev) for rev in heads], inclusive=True) 394 heads_anc = cl.ancestors([cl.rev(rev) for rev in heads], inclusive=True)
395 common_anc = cl.ancestors([cl.rev(rev) for rev in common], inclusive=True) 395 common_anc = cl.ancestors([cl.rev(rev) for rev in common], inclusive=True)
396 compformats = clientcompressionsupport(proto) 396 compformats = clientcompressionsupport(proto)
397 for entry in res: 397 for entry in res:
398 comp = entry.get('COMPRESSION') 398 comp = entry.get(b'COMPRESSION')
399 altcomp = util.compengines._bundlenames.get(comp) 399 altcomp = util.compengines._bundlenames.get(comp)
400 if comp and comp not in compformats and altcomp not in compformats: 400 if comp and comp not in compformats and altcomp not in compformats:
401 continue 401 continue
402 # No test yet for VERSION, since V2 is supported by any client 402 # No test yet for VERSION, since V2 is supported by any client
403 # that advertises partial pulls 403 # that advertises partial pulls
404 if 'heads' in entry: 404 if b'heads' in entry:
405 try: 405 try:
406 bundle_heads = decodehexstring(entry['heads']) 406 bundle_heads = decodehexstring(entry[b'heads'])
407 except TypeError: 407 except TypeError:
408 # Bad heads entry 408 # Bad heads entry
409 continue 409 continue
410 if bundle_heads.issubset(common): 410 if bundle_heads.issubset(common):
411 continue # Nothing new 411 continue # Nothing new
414 if any( 414 if any(
415 cl.rev(rev) not in heads_anc and cl.rev(rev) not in common_anc 415 cl.rev(rev) not in heads_anc and cl.rev(rev) not in common_anc
416 for rev in bundle_heads 416 for rev in bundle_heads
417 ): 417 ):
418 continue 418 continue
419 if 'bases' in entry: 419 if b'bases' in entry:
420 try: 420 try:
421 bundle_bases = decodehexstring(entry['bases']) 421 bundle_bases = decodehexstring(entry[b'bases'])
422 except TypeError: 422 except TypeError:
423 # Bad bases entry 423 # Bad bases entry
424 continue 424 continue
425 if not all(cl.rev(rev) in common_anc for rev in bundle_bases): 425 if not all(cl.rev(rev) in common_anc for rev in bundle_bases):
426 continue 426 continue
427 path = entry['URL'] 427 path = entry[b'URL']
428 repo.ui.debug('sending pullbundle "%s"\n' % path) 428 repo.ui.debug(b'sending pullbundle "%s"\n' % path)
429 try: 429 try:
430 return repo.vfs.open(path) 430 return repo.vfs.open(path)
431 except IOError: 431 except IOError:
432 repo.ui.debug('pullbundle "%s" not accessible\n' % path) 432 repo.ui.debug(b'pullbundle "%s" not accessible\n' % path)
433 continue 433 continue
434 return None 434 return None
435 435
436 436
437 @wireprotocommand('getbundle', '*', permission='pull') 437 @wireprotocommand(b'getbundle', b'*', permission=b'pull')
438 def getbundle(repo, proto, others): 438 def getbundle(repo, proto, others):
439 opts = options( 439 opts = options(
440 'getbundle', wireprototypes.GETBUNDLE_ARGUMENTS.keys(), others 440 b'getbundle', wireprototypes.GETBUNDLE_ARGUMENTS.keys(), others
441 ) 441 )
442 for k, v in opts.iteritems(): 442 for k, v in opts.iteritems():
443 keytype = wireprototypes.GETBUNDLE_ARGUMENTS[k] 443 keytype = wireprototypes.GETBUNDLE_ARGUMENTS[k]
444 if keytype == 'nodes': 444 if keytype == b'nodes':
445 opts[k] = wireprototypes.decodelist(v) 445 opts[k] = wireprototypes.decodelist(v)
446 elif keytype == 'csv': 446 elif keytype == b'csv':
447 opts[k] = list(v.split(',')) 447 opts[k] = list(v.split(b','))
448 elif keytype == 'scsv': 448 elif keytype == b'scsv':
449 opts[k] = set(v.split(',')) 449 opts[k] = set(v.split(b','))
450 elif keytype == 'boolean': 450 elif keytype == b'boolean':
451 # Client should serialize False as '0', which is a non-empty string 451 # Client should serialize False as '0', which is a non-empty string
452 # so it evaluates as a True bool. 452 # so it evaluates as a True bool.
453 if v == '0': 453 if v == b'0':
454 opts[k] = False 454 opts[k] = False
455 else: 455 else:
456 opts[k] = bool(v) 456 opts[k] = bool(v)
457 elif keytype != 'plain': 457 elif keytype != b'plain':
458 raise KeyError('unknown getbundle option type %s' % keytype) 458 raise KeyError(b'unknown getbundle option type %s' % keytype)
459 459
460 if not bundle1allowed(repo, 'pull'): 460 if not bundle1allowed(repo, b'pull'):
461 if not exchange.bundle2requested(opts.get('bundlecaps')): 461 if not exchange.bundle2requested(opts.get(b'bundlecaps')):
462 if proto.name == 'http-v1': 462 if proto.name == b'http-v1':
463 return wireprototypes.ooberror(bundle2required) 463 return wireprototypes.ooberror(bundle2required)
464 raise error.Abort(bundle2requiredmain, hint=bundle2requiredhint) 464 raise error.Abort(bundle2requiredmain, hint=bundle2requiredhint)
465 465
466 try: 466 try:
467 clheads = set(repo.changelog.heads()) 467 clheads = set(repo.changelog.heads())
468 heads = set(opts.get('heads', set())) 468 heads = set(opts.get(b'heads', set()))
469 common = set(opts.get('common', set())) 469 common = set(opts.get(b'common', set()))
470 common.discard(nullid) 470 common.discard(nullid)
471 if ( 471 if (
472 repo.ui.configbool('server', 'pullbundle') 472 repo.ui.configbool(b'server', b'pullbundle')
473 and 'partial-pull' in proto.getprotocaps() 473 and b'partial-pull' in proto.getprotocaps()
474 ): 474 ):
475 # Check if a pre-built bundle covers this request. 475 # Check if a pre-built bundle covers this request.
476 bundle = find_pullbundle(repo, proto, opts, clheads, heads, common) 476 bundle = find_pullbundle(repo, proto, opts, clheads, heads, common)
477 if bundle: 477 if bundle:
478 return wireprototypes.streamres( 478 return wireprototypes.streamres(
479 gen=util.filechunkiter(bundle), prefer_uncompressed=True 479 gen=util.filechunkiter(bundle), prefer_uncompressed=True
480 ) 480 )
481 481
482 if repo.ui.configbool('server', 'disablefullbundle'): 482 if repo.ui.configbool(b'server', b'disablefullbundle'):
483 # Check to see if this is a full clone. 483 # Check to see if this is a full clone.
484 changegroup = opts.get('cg', True) 484 changegroup = opts.get(b'cg', True)
485 if changegroup and not common and clheads == heads: 485 if changegroup and not common and clheads == heads:
486 raise error.Abort( 486 raise error.Abort(
487 _('server has pull-based clones disabled'), 487 _(b'server has pull-based clones disabled'),
488 hint=_('remove --pull if specified or upgrade Mercurial'), 488 hint=_(b'remove --pull if specified or upgrade Mercurial'),
489 ) 489 )
490 490
491 info, chunks = exchange.getbundlechunks( 491 info, chunks = exchange.getbundlechunks(
492 repo, 'serve', **pycompat.strkwargs(opts) 492 repo, b'serve', **pycompat.strkwargs(opts)
493 ) 493 )
494 prefercompressed = info.get('prefercompressed', True) 494 prefercompressed = info.get(b'prefercompressed', True)
495 except error.Abort as exc: 495 except error.Abort as exc:
496 # cleanly forward Abort error to the client 496 # cleanly forward Abort error to the client
497 if not exchange.bundle2requested(opts.get('bundlecaps')): 497 if not exchange.bundle2requested(opts.get(b'bundlecaps')):
498 if proto.name == 'http-v1': 498 if proto.name == b'http-v1':
499 return wireprototypes.ooberror(pycompat.bytestr(exc) + '\n') 499 return wireprototypes.ooberror(pycompat.bytestr(exc) + b'\n')
500 raise # cannot do better for bundle1 + ssh 500 raise # cannot do better for bundle1 + ssh
501 # bundle2 request expect a bundle2 reply 501 # bundle2 request expect a bundle2 reply
502 bundler = bundle2.bundle20(repo.ui) 502 bundler = bundle2.bundle20(repo.ui)
503 manargs = [('message', pycompat.bytestr(exc))] 503 manargs = [(b'message', pycompat.bytestr(exc))]
504 advargs = [] 504 advargs = []
505 if exc.hint is not None: 505 if exc.hint is not None:
506 advargs.append(('hint', exc.hint)) 506 advargs.append((b'hint', exc.hint))
507 bundler.addpart(bundle2.bundlepart('error:abort', manargs, advargs)) 507 bundler.addpart(bundle2.bundlepart(b'error:abort', manargs, advargs))
508 chunks = bundler.getchunks() 508 chunks = bundler.getchunks()
509 prefercompressed = False 509 prefercompressed = False
510 510
511 return wireprototypes.streamres( 511 return wireprototypes.streamres(
512 gen=chunks, prefer_uncompressed=not prefercompressed 512 gen=chunks, prefer_uncompressed=not prefercompressed
513 ) 513 )
514 514
515 515
516 @wireprotocommand('heads', permission='pull') 516 @wireprotocommand(b'heads', permission=b'pull')
517 def heads(repo, proto): 517 def heads(repo, proto):
518 h = repo.heads() 518 h = repo.heads()
519 return wireprototypes.bytesresponse(wireprototypes.encodelist(h) + '\n') 519 return wireprototypes.bytesresponse(wireprototypes.encodelist(h) + b'\n')
520 520
521 521
522 @wireprotocommand('hello', permission='pull') 522 @wireprotocommand(b'hello', permission=b'pull')
523 def hello(repo, proto): 523 def hello(repo, proto):
524 """Called as part of SSH handshake to obtain server info. 524 """Called as part of SSH handshake to obtain server info.
525 525
526 Returns a list of lines describing interesting things about the 526 Returns a list of lines describing interesting things about the
527 server, in an RFC822-like format. 527 server, in an RFC822-like format.
530 line of space separated tokens describing server abilities: 530 line of space separated tokens describing server abilities:
531 531
532 capabilities: <token0> <token1> <token2> 532 capabilities: <token0> <token1> <token2>
533 """ 533 """
534 caps = capabilities(repo, proto).data 534 caps = capabilities(repo, proto).data
535 return wireprototypes.bytesresponse('capabilities: %s\n' % caps) 535 return wireprototypes.bytesresponse(b'capabilities: %s\n' % caps)
536 536
537 537
538 @wireprotocommand('listkeys', 'namespace', permission='pull') 538 @wireprotocommand(b'listkeys', b'namespace', permission=b'pull')
539 def listkeys(repo, proto, namespace): 539 def listkeys(repo, proto, namespace):
540 d = sorted(repo.listkeys(encoding.tolocal(namespace)).items()) 540 d = sorted(repo.listkeys(encoding.tolocal(namespace)).items())
541 return wireprototypes.bytesresponse(pushkeymod.encodekeys(d)) 541 return wireprototypes.bytesresponse(pushkeymod.encodekeys(d))
542 542
543 543
544 @wireprotocommand('lookup', 'key', permission='pull') 544 @wireprotocommand(b'lookup', b'key', permission=b'pull')
545 def lookup(repo, proto, key): 545 def lookup(repo, proto, key):
546 try: 546 try:
547 k = encoding.tolocal(key) 547 k = encoding.tolocal(key)
548 n = repo.lookup(k) 548 n = repo.lookup(k)
549 r = hex(n) 549 r = hex(n)
550 success = 1 550 success = 1
551 except Exception as inst: 551 except Exception as inst:
552 r = stringutil.forcebytestr(inst) 552 r = stringutil.forcebytestr(inst)
553 success = 0 553 success = 0
554 return wireprototypes.bytesresponse('%d %s\n' % (success, r)) 554 return wireprototypes.bytesresponse(b'%d %s\n' % (success, r))
555 555
556 556
557 @wireprotocommand('known', 'nodes *', permission='pull') 557 @wireprotocommand(b'known', b'nodes *', permission=b'pull')
558 def known(repo, proto, nodes, others): 558 def known(repo, proto, nodes, others):
559 v = ''.join( 559 v = b''.join(
560 b and '1' or '0' for b in repo.known(wireprototypes.decodelist(nodes)) 560 b and b'1' or b'0' for b in repo.known(wireprototypes.decodelist(nodes))
561 ) 561 )
562 return wireprototypes.bytesresponse(v) 562 return wireprototypes.bytesresponse(v)
563 563
564 564
565 @wireprotocommand('protocaps', 'caps', permission='pull') 565 @wireprotocommand(b'protocaps', b'caps', permission=b'pull')
566 def protocaps(repo, proto, caps): 566 def protocaps(repo, proto, caps):
567 if proto.name == wireprototypes.SSHV1: 567 if proto.name == wireprototypes.SSHV1:
568 proto._protocaps = set(caps.split(' ')) 568 proto._protocaps = set(caps.split(b' '))
569 return wireprototypes.bytesresponse('OK') 569 return wireprototypes.bytesresponse(b'OK')
570 570
571 571
572 @wireprotocommand('pushkey', 'namespace key old new', permission='push') 572 @wireprotocommand(b'pushkey', b'namespace key old new', permission=b'push')
573 def pushkey(repo, proto, namespace, key, old, new): 573 def pushkey(repo, proto, namespace, key, old, new):
574 # compatibility with pre-1.8 clients which were accidentally 574 # compatibility with pre-1.8 clients which were accidentally
575 # sending raw binary nodes rather than utf-8-encoded hex 575 # sending raw binary nodes rather than utf-8-encoded hex
576 if len(new) == 20 and stringutil.escapestr(new) != new: 576 if len(new) == 20 and stringutil.escapestr(new) != new:
577 # looks like it could be a binary node 577 # looks like it could be a binary node
592 new, 592 new,
593 ) 593 )
594 or False 594 or False
595 ) 595 )
596 596
597 output = output.getvalue() if output else '' 597 output = output.getvalue() if output else b''
598 return wireprototypes.bytesresponse('%d\n%s' % (int(r), output)) 598 return wireprototypes.bytesresponse(b'%d\n%s' % (int(r), output))
599 599
600 600
601 @wireprotocommand('stream_out', permission='pull') 601 @wireprotocommand(b'stream_out', permission=b'pull')
602 def stream(repo, proto): 602 def stream(repo, proto):
603 '''If the server supports streaming clone, it advertises the "stream" 603 '''If the server supports streaming clone, it advertises the "stream"
604 capability with a value representing the version and flags of the repo 604 capability with a value representing the version and flags of the repo
605 it is serving. Client checks to see if it understands the format. 605 it is serving. Client checks to see if it understands the format.
606 ''' 606 '''
607 return wireprototypes.streamreslegacy(streamclone.generatev1wireproto(repo)) 607 return wireprototypes.streamreslegacy(streamclone.generatev1wireproto(repo))
608 608
609 609
610 @wireprotocommand('unbundle', 'heads', permission='push') 610 @wireprotocommand(b'unbundle', b'heads', permission=b'push')
611 def unbundle(repo, proto, heads): 611 def unbundle(repo, proto, heads):
612 their_heads = wireprototypes.decodelist(heads) 612 their_heads = wireprototypes.decodelist(heads)
613 613
614 with proto.mayberedirectstdio() as output: 614 with proto.mayberedirectstdio() as output:
615 try: 615 try:
616 exchange.check_heads(repo, their_heads, 'preparing changes') 616 exchange.check_heads(repo, their_heads, b'preparing changes')
617 cleanup = lambda: None 617 cleanup = lambda: None
618 try: 618 try:
619 payload = proto.getpayload() 619 payload = proto.getpayload()
620 if repo.ui.configbool('server', 'streamunbundle'): 620 if repo.ui.configbool(b'server', b'streamunbundle'):
621 621
622 def cleanup(): 622 def cleanup():
623 # Ensure that the full payload is consumed, so 623 # Ensure that the full payload is consumed, so
624 # that the connection doesn't contain trailing garbage. 624 # that the connection doesn't contain trailing garbage.
625 for p in payload: 625 for p in payload:
634 if fp: 634 if fp:
635 fp.close() 635 fp.close()
636 if tempname: 636 if tempname:
637 os.unlink(tempname) 637 os.unlink(tempname)
638 638
639 fd, tempname = pycompat.mkstemp(prefix='hg-unbundle-') 639 fd, tempname = pycompat.mkstemp(prefix=b'hg-unbundle-')
640 repo.ui.debug( 640 repo.ui.debug(
641 'redirecting incoming bundle to %s\n' % tempname 641 b'redirecting incoming bundle to %s\n' % tempname
642 ) 642 )
643 fp = os.fdopen(fd, pycompat.sysstr('wb+')) 643 fp = os.fdopen(fd, pycompat.sysstr(b'wb+'))
644 for p in payload: 644 for p in payload:
645 fp.write(p) 645 fp.write(p)
646 fp.seek(0) 646 fp.seek(0)
647 647
648 gen = exchange.readbundle(repo.ui, fp, None) 648 gen = exchange.readbundle(repo.ui, fp, None)
649 if isinstance( 649 if isinstance(
650 gen, changegroupmod.cg1unpacker 650 gen, changegroupmod.cg1unpacker
651 ) and not bundle1allowed(repo, 'push'): 651 ) and not bundle1allowed(repo, b'push'):
652 if proto.name == 'http-v1': 652 if proto.name == b'http-v1':
653 # need to special case http because stderr do not get to 653 # need to special case http because stderr do not get to
654 # the http client on failed push so we need to abuse 654 # the http client on failed push so we need to abuse
655 # some other error type to make sure the message get to 655 # some other error type to make sure the message get to
656 # the user. 656 # the user.
657 return wireprototypes.ooberror(bundle2required) 657 return wireprototypes.ooberror(bundle2required)
658 raise error.Abort( 658 raise error.Abort(
659 bundle2requiredmain, hint=bundle2requiredhint 659 bundle2requiredmain, hint=bundle2requiredhint
660 ) 660 )
661 661
662 r = exchange.unbundle( 662 r = exchange.unbundle(
663 repo, gen, their_heads, 'serve', proto.client() 663 repo, gen, their_heads, b'serve', proto.client()
664 ) 664 )
665 if util.safehasattr(r, 'addpart'): 665 if util.safehasattr(r, b'addpart'):
666 # The return looks streamable, we are in the bundle2 case 666 # The return looks streamable, we are in the bundle2 case
667 # and should return a stream. 667 # and should return a stream.
668 return wireprototypes.streamreslegacy(gen=r.getchunks()) 668 return wireprototypes.streamreslegacy(gen=r.getchunks())
669 return wireprototypes.pushres( 669 return wireprototypes.pushres(
670 r, output.getvalue() if output else '' 670 r, output.getvalue() if output else b''
671 ) 671 )
672 672
673 finally: 673 finally:
674 cleanup() 674 cleanup()
675 675
681 except error.Abort: 681 except error.Abort:
682 # The old code we moved used procutil.stderr directly. 682 # The old code we moved used procutil.stderr directly.
683 # We did not change it to minimise code change. 683 # We did not change it to minimise code change.
684 # This need to be moved to something proper. 684 # This need to be moved to something proper.
685 # Feel free to do it. 685 # Feel free to do it.
686 procutil.stderr.write("abort: %s\n" % exc) 686 procutil.stderr.write(b"abort: %s\n" % exc)
687 if exc.hint is not None: 687 if exc.hint is not None:
688 procutil.stderr.write("(%s)\n" % exc.hint) 688 procutil.stderr.write(b"(%s)\n" % exc.hint)
689 procutil.stderr.flush() 689 procutil.stderr.flush()
690 return wireprototypes.pushres( 690 return wireprototypes.pushres(
691 0, output.getvalue() if output else '' 691 0, output.getvalue() if output else b''
692 ) 692 )
693 except error.PushRaced: 693 except error.PushRaced:
694 return wireprototypes.pusherr( 694 return wireprototypes.pusherr(
695 pycompat.bytestr(exc), 695 pycompat.bytestr(exc),
696 output.getvalue() if output else '', 696 output.getvalue() if output else b'',
697 ) 697 )
698 698
699 bundler = bundle2.bundle20(repo.ui) 699 bundler = bundle2.bundle20(repo.ui)
700 for out in getattr(exc, '_bundle2salvagedoutput', ()): 700 for out in getattr(exc, '_bundle2salvagedoutput', ()):
701 bundler.addpart(out) 701 bundler.addpart(out)
705 except error.PushkeyFailed as exc: 705 except error.PushkeyFailed as exc:
706 # check client caps 706 # check client caps
707 remotecaps = getattr(exc, '_replycaps', None) 707 remotecaps = getattr(exc, '_replycaps', None)
708 if ( 708 if (
709 remotecaps is not None 709 remotecaps is not None
710 and 'pushkey' not in remotecaps.get('error', ()) 710 and b'pushkey' not in remotecaps.get(b'error', ())
711 ): 711 ):
712 # no support remote side, fallback to Abort handler. 712 # no support remote side, fallback to Abort handler.
713 raise 713 raise
714 part = bundler.newpart('error:pushkey') 714 part = bundler.newpart(b'error:pushkey')
715 part.addparam('in-reply-to', exc.partid) 715 part.addparam(b'in-reply-to', exc.partid)
716 if exc.namespace is not None: 716 if exc.namespace is not None:
717 part.addparam( 717 part.addparam(
718 'namespace', exc.namespace, mandatory=False 718 b'namespace', exc.namespace, mandatory=False
719 ) 719 )
720 if exc.key is not None: 720 if exc.key is not None:
721 part.addparam('key', exc.key, mandatory=False) 721 part.addparam(b'key', exc.key, mandatory=False)
722 if exc.new is not None: 722 if exc.new is not None:
723 part.addparam('new', exc.new, mandatory=False) 723 part.addparam(b'new', exc.new, mandatory=False)
724 if exc.old is not None: 724 if exc.old is not None:
725 part.addparam('old', exc.old, mandatory=False) 725 part.addparam(b'old', exc.old, mandatory=False)
726 if exc.ret is not None: 726 if exc.ret is not None:
727 part.addparam('ret', exc.ret, mandatory=False) 727 part.addparam(b'ret', exc.ret, mandatory=False)
728 except error.BundleValueError as exc: 728 except error.BundleValueError as exc:
729 errpart = bundler.newpart('error:unsupportedcontent') 729 errpart = bundler.newpart(b'error:unsupportedcontent')
730 if exc.parttype is not None: 730 if exc.parttype is not None:
731 errpart.addparam('parttype', exc.parttype) 731 errpart.addparam(b'parttype', exc.parttype)
732 if exc.params: 732 if exc.params:
733 errpart.addparam('params', '\0'.join(exc.params)) 733 errpart.addparam(b'params', b'\0'.join(exc.params))
734 except error.Abort as exc: 734 except error.Abort as exc:
735 manargs = [('message', stringutil.forcebytestr(exc))] 735 manargs = [(b'message', stringutil.forcebytestr(exc))]
736 advargs = [] 736 advargs = []
737 if exc.hint is not None: 737 if exc.hint is not None:
738 advargs.append(('hint', exc.hint)) 738 advargs.append((b'hint', exc.hint))
739 bundler.addpart( 739 bundler.addpart(
740 bundle2.bundlepart('error:abort', manargs, advargs) 740 bundle2.bundlepart(b'error:abort', manargs, advargs)
741 ) 741 )
742 except error.PushRaced as exc: 742 except error.PushRaced as exc:
743 bundler.newpart( 743 bundler.newpart(
744 'error:pushraced', 744 b'error:pushraced',
745 [('message', stringutil.forcebytestr(exc))], 745 [(b'message', stringutil.forcebytestr(exc))],
746 ) 746 )
747 return wireprototypes.streamreslegacy(gen=bundler.getchunks()) 747 return wireprototypes.streamreslegacy(gen=bundler.getchunks())