##// END OF EJS Templates
sshpeer: initial definition and implementation of new SSH protocol...
Gregory Szorc -
r35994:48a3a928 default
parent child Browse files
Show More
@@ -1,1305 +1,1308 b''
1 1 # configitems.py - centralized declaration of configuration option
2 2 #
3 3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import functools
11 11 import re
12 12
13 13 from . import (
14 14 encoding,
15 15 error,
16 16 )
17 17
18 18 def loadconfigtable(ui, extname, configtable):
19 19 """update config item known to the ui with the extension ones"""
20 20 for section, items in sorted(configtable.items()):
21 21 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 22 knownkeys = set(knownitems)
23 23 newkeys = set(items)
24 24 for key in sorted(knownkeys & newkeys):
25 25 msg = "extension '%s' overwrite config item '%s.%s'"
26 26 msg %= (extname, section, key)
27 27 ui.develwarn(msg, config='warn-config')
28 28
29 29 knownitems.update(items)
30 30
31 31 class configitem(object):
32 32 """represent a known config item
33 33
34 34 :section: the official config section where to find this item,
35 35 :name: the official name within the section,
36 36 :default: default value for this item,
37 37 :alias: optional list of tuples as alternatives,
38 38 :generic: this is a generic definition, match name using regular expression.
39 39 """
40 40
41 41 def __init__(self, section, name, default=None, alias=(),
42 42 generic=False, priority=0):
43 43 self.section = section
44 44 self.name = name
45 45 self.default = default
46 46 self.alias = list(alias)
47 47 self.generic = generic
48 48 self.priority = priority
49 49 self._re = None
50 50 if generic:
51 51 self._re = re.compile(self.name)
52 52
53 53 class itemregister(dict):
54 54 """A specialized dictionary that can handle wild-card selection"""
55 55
56 56 def __init__(self):
57 57 super(itemregister, self).__init__()
58 58 self._generics = set()
59 59
60 60 def update(self, other):
61 61 super(itemregister, self).update(other)
62 62 self._generics.update(other._generics)
63 63
64 64 def __setitem__(self, key, item):
65 65 super(itemregister, self).__setitem__(key, item)
66 66 if item.generic:
67 67 self._generics.add(item)
68 68
69 69 def get(self, key):
70 70 baseitem = super(itemregister, self).get(key)
71 71 if baseitem is not None and not baseitem.generic:
72 72 return baseitem
73 73
74 74 # search for a matching generic item
75 75 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
76 76 for item in generics:
77 77 # we use 'match' instead of 'search' to make the matching simpler
78 78 # for people unfamiliar with regular expression. Having the match
79 79 # rooted to the start of the string will produce less surprising
80 80 # result for user writing simple regex for sub-attribute.
81 81 #
82 82 # For example using "color\..*" match produces an unsurprising
83 83 # result, while using search could suddenly match apparently
84 84 # unrelated configuration that happens to contains "color."
85 85 # anywhere. This is a tradeoff where we favor requiring ".*" on
86 86 # some match to avoid the need to prefix most pattern with "^".
87 87 # The "^" seems more error prone.
88 88 if item._re.match(key):
89 89 return item
90 90
91 91 return None
92 92
93 93 coreitems = {}
94 94
95 95 def _register(configtable, *args, **kwargs):
96 96 item = configitem(*args, **kwargs)
97 97 section = configtable.setdefault(item.section, itemregister())
98 98 if item.name in section:
99 99 msg = "duplicated config item registration for '%s.%s'"
100 100 raise error.ProgrammingError(msg % (item.section, item.name))
101 101 section[item.name] = item
102 102
103 103 # special value for case where the default is derived from other values
104 104 dynamicdefault = object()
105 105
106 106 # Registering actual config items
107 107
108 108 def getitemregister(configtable):
109 109 f = functools.partial(_register, configtable)
110 110 # export pseudo enum as configitem.*
111 111 f.dynamicdefault = dynamicdefault
112 112 return f
113 113
114 114 coreconfigitem = getitemregister(coreitems)
115 115
116 116 coreconfigitem('alias', '.*',
117 117 default=None,
118 118 generic=True,
119 119 )
120 120 coreconfigitem('annotate', 'nodates',
121 121 default=False,
122 122 )
123 123 coreconfigitem('annotate', 'showfunc',
124 124 default=False,
125 125 )
126 126 coreconfigitem('annotate', 'unified',
127 127 default=None,
128 128 )
129 129 coreconfigitem('annotate', 'git',
130 130 default=False,
131 131 )
132 132 coreconfigitem('annotate', 'ignorews',
133 133 default=False,
134 134 )
135 135 coreconfigitem('annotate', 'ignorewsamount',
136 136 default=False,
137 137 )
138 138 coreconfigitem('annotate', 'ignoreblanklines',
139 139 default=False,
140 140 )
141 141 coreconfigitem('annotate', 'ignorewseol',
142 142 default=False,
143 143 )
144 144 coreconfigitem('annotate', 'nobinary',
145 145 default=False,
146 146 )
147 147 coreconfigitem('annotate', 'noprefix',
148 148 default=False,
149 149 )
150 150 coreconfigitem('auth', 'cookiefile',
151 151 default=None,
152 152 )
153 153 # bookmarks.pushing: internal hack for discovery
154 154 coreconfigitem('bookmarks', 'pushing',
155 155 default=list,
156 156 )
157 157 # bundle.mainreporoot: internal hack for bundlerepo
158 158 coreconfigitem('bundle', 'mainreporoot',
159 159 default='',
160 160 )
161 161 # bundle.reorder: experimental config
162 162 coreconfigitem('bundle', 'reorder',
163 163 default='auto',
164 164 )
165 165 coreconfigitem('censor', 'policy',
166 166 default='abort',
167 167 )
168 168 coreconfigitem('chgserver', 'idletimeout',
169 169 default=3600,
170 170 )
171 171 coreconfigitem('chgserver', 'skiphash',
172 172 default=False,
173 173 )
174 174 coreconfigitem('cmdserver', 'log',
175 175 default=None,
176 176 )
177 177 coreconfigitem('color', '.*',
178 178 default=None,
179 179 generic=True,
180 180 )
181 181 coreconfigitem('color', 'mode',
182 182 default='auto',
183 183 )
184 184 coreconfigitem('color', 'pagermode',
185 185 default=dynamicdefault,
186 186 )
187 187 coreconfigitem('commands', 'show.aliasprefix',
188 188 default=list,
189 189 )
190 190 coreconfigitem('commands', 'status.relative',
191 191 default=False,
192 192 )
193 193 coreconfigitem('commands', 'status.skipstates',
194 194 default=[],
195 195 )
196 196 coreconfigitem('commands', 'status.verbose',
197 197 default=False,
198 198 )
199 199 coreconfigitem('commands', 'update.check',
200 200 default=None,
201 201 # Deprecated, remove after 4.4 release
202 202 alias=[('experimental', 'updatecheck')]
203 203 )
204 204 coreconfigitem('commands', 'update.requiredest',
205 205 default=False,
206 206 )
207 207 coreconfigitem('committemplate', '.*',
208 208 default=None,
209 209 generic=True,
210 210 )
211 211 coreconfigitem('convert', 'cvsps.cache',
212 212 default=True,
213 213 )
214 214 coreconfigitem('convert', 'cvsps.fuzz',
215 215 default=60,
216 216 )
217 217 coreconfigitem('convert', 'cvsps.logencoding',
218 218 default=None,
219 219 )
220 220 coreconfigitem('convert', 'cvsps.mergefrom',
221 221 default=None,
222 222 )
223 223 coreconfigitem('convert', 'cvsps.mergeto',
224 224 default=None,
225 225 )
226 226 coreconfigitem('convert', 'git.committeractions',
227 227 default=lambda: ['messagedifferent'],
228 228 )
229 229 coreconfigitem('convert', 'git.extrakeys',
230 230 default=list,
231 231 )
232 232 coreconfigitem('convert', 'git.findcopiesharder',
233 233 default=False,
234 234 )
235 235 coreconfigitem('convert', 'git.remoteprefix',
236 236 default='remote',
237 237 )
238 238 coreconfigitem('convert', 'git.renamelimit',
239 239 default=400,
240 240 )
241 241 coreconfigitem('convert', 'git.saverev',
242 242 default=True,
243 243 )
244 244 coreconfigitem('convert', 'git.similarity',
245 245 default=50,
246 246 )
247 247 coreconfigitem('convert', 'git.skipsubmodules',
248 248 default=False,
249 249 )
250 250 coreconfigitem('convert', 'hg.clonebranches',
251 251 default=False,
252 252 )
253 253 coreconfigitem('convert', 'hg.ignoreerrors',
254 254 default=False,
255 255 )
256 256 coreconfigitem('convert', 'hg.revs',
257 257 default=None,
258 258 )
259 259 coreconfigitem('convert', 'hg.saverev',
260 260 default=False,
261 261 )
262 262 coreconfigitem('convert', 'hg.sourcename',
263 263 default=None,
264 264 )
265 265 coreconfigitem('convert', 'hg.startrev',
266 266 default=None,
267 267 )
268 268 coreconfigitem('convert', 'hg.tagsbranch',
269 269 default='default',
270 270 )
271 271 coreconfigitem('convert', 'hg.usebranchnames',
272 272 default=True,
273 273 )
274 274 coreconfigitem('convert', 'ignoreancestorcheck',
275 275 default=False,
276 276 )
277 277 coreconfigitem('convert', 'localtimezone',
278 278 default=False,
279 279 )
280 280 coreconfigitem('convert', 'p4.encoding',
281 281 default=dynamicdefault,
282 282 )
283 283 coreconfigitem('convert', 'p4.startrev',
284 284 default=0,
285 285 )
286 286 coreconfigitem('convert', 'skiptags',
287 287 default=False,
288 288 )
289 289 coreconfigitem('convert', 'svn.debugsvnlog',
290 290 default=True,
291 291 )
292 292 coreconfigitem('convert', 'svn.trunk',
293 293 default=None,
294 294 )
295 295 coreconfigitem('convert', 'svn.tags',
296 296 default=None,
297 297 )
298 298 coreconfigitem('convert', 'svn.branches',
299 299 default=None,
300 300 )
301 301 coreconfigitem('convert', 'svn.startrev',
302 302 default=0,
303 303 )
304 304 coreconfigitem('debug', 'dirstate.delaywrite',
305 305 default=0,
306 306 )
307 307 coreconfigitem('defaults', '.*',
308 308 default=None,
309 309 generic=True,
310 310 )
311 311 coreconfigitem('devel', 'all-warnings',
312 312 default=False,
313 313 )
314 314 coreconfigitem('devel', 'bundle2.debug',
315 315 default=False,
316 316 )
317 317 coreconfigitem('devel', 'cache-vfs',
318 318 default=None,
319 319 )
320 320 coreconfigitem('devel', 'check-locks',
321 321 default=False,
322 322 )
323 323 coreconfigitem('devel', 'check-relroot',
324 324 default=False,
325 325 )
326 326 coreconfigitem('devel', 'default-date',
327 327 default=None,
328 328 )
329 329 coreconfigitem('devel', 'deprec-warn',
330 330 default=False,
331 331 )
332 332 coreconfigitem('devel', 'disableloaddefaultcerts',
333 333 default=False,
334 334 )
335 335 coreconfigitem('devel', 'warn-empty-changegroup',
336 336 default=False,
337 337 )
338 338 coreconfigitem('devel', 'legacy.exchange',
339 339 default=list,
340 340 )
341 341 coreconfigitem('devel', 'servercafile',
342 342 default='',
343 343 )
344 344 coreconfigitem('devel', 'serverexactprotocol',
345 345 default='',
346 346 )
347 347 coreconfigitem('devel', 'serverrequirecert',
348 348 default=False,
349 349 )
350 350 coreconfigitem('devel', 'strip-obsmarkers',
351 351 default=True,
352 352 )
353 353 coreconfigitem('devel', 'warn-config',
354 354 default=None,
355 355 )
356 356 coreconfigitem('devel', 'warn-config-default',
357 357 default=None,
358 358 )
359 359 coreconfigitem('devel', 'user.obsmarker',
360 360 default=None,
361 361 )
362 362 coreconfigitem('devel', 'warn-config-unknown',
363 363 default=None,
364 364 )
365 365 coreconfigitem('devel', 'debug.peer-request',
366 366 default=False,
367 367 )
368 368 coreconfigitem('diff', 'nodates',
369 369 default=False,
370 370 )
371 371 coreconfigitem('diff', 'showfunc',
372 372 default=False,
373 373 )
374 374 coreconfigitem('diff', 'unified',
375 375 default=None,
376 376 )
377 377 coreconfigitem('diff', 'git',
378 378 default=False,
379 379 )
380 380 coreconfigitem('diff', 'ignorews',
381 381 default=False,
382 382 )
383 383 coreconfigitem('diff', 'ignorewsamount',
384 384 default=False,
385 385 )
386 386 coreconfigitem('diff', 'ignoreblanklines',
387 387 default=False,
388 388 )
389 389 coreconfigitem('diff', 'ignorewseol',
390 390 default=False,
391 391 )
392 392 coreconfigitem('diff', 'nobinary',
393 393 default=False,
394 394 )
395 395 coreconfigitem('diff', 'noprefix',
396 396 default=False,
397 397 )
398 398 coreconfigitem('email', 'bcc',
399 399 default=None,
400 400 )
401 401 coreconfigitem('email', 'cc',
402 402 default=None,
403 403 )
404 404 coreconfigitem('email', 'charsets',
405 405 default=list,
406 406 )
407 407 coreconfigitem('email', 'from',
408 408 default=None,
409 409 )
410 410 coreconfigitem('email', 'method',
411 411 default='smtp',
412 412 )
413 413 coreconfigitem('email', 'reply-to',
414 414 default=None,
415 415 )
416 416 coreconfigitem('email', 'to',
417 417 default=None,
418 418 )
419 419 coreconfigitem('experimental', 'archivemetatemplate',
420 420 default=dynamicdefault,
421 421 )
422 422 coreconfigitem('experimental', 'bundle-phases',
423 423 default=False,
424 424 )
425 425 coreconfigitem('experimental', 'bundle2-advertise',
426 426 default=True,
427 427 )
428 428 coreconfigitem('experimental', 'bundle2-output-capture',
429 429 default=False,
430 430 )
431 431 coreconfigitem('experimental', 'bundle2.pushback',
432 432 default=False,
433 433 )
434 434 coreconfigitem('experimental', 'bundle2.stream',
435 435 default=False,
436 436 )
437 437 coreconfigitem('experimental', 'bundle2lazylocking',
438 438 default=False,
439 439 )
440 440 coreconfigitem('experimental', 'bundlecomplevel',
441 441 default=None,
442 442 )
443 443 coreconfigitem('experimental', 'changegroup3',
444 444 default=False,
445 445 )
446 446 coreconfigitem('experimental', 'clientcompressionengines',
447 447 default=list,
448 448 )
449 449 coreconfigitem('experimental', 'copytrace',
450 450 default='on',
451 451 )
452 452 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
453 453 default=100,
454 454 )
455 455 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
456 456 default=100,
457 457 )
458 458 coreconfigitem('experimental', 'crecordtest',
459 459 default=None,
460 460 )
461 461 coreconfigitem('experimental', 'directaccess',
462 462 default=False,
463 463 )
464 464 coreconfigitem('experimental', 'directaccess.revnums',
465 465 default=False,
466 466 )
467 467 coreconfigitem('experimental', 'editortmpinhg',
468 468 default=False,
469 469 )
470 470 coreconfigitem('experimental', 'evolution',
471 471 default=list,
472 472 )
473 473 coreconfigitem('experimental', 'evolution.allowdivergence',
474 474 default=False,
475 475 alias=[('experimental', 'allowdivergence')]
476 476 )
477 477 coreconfigitem('experimental', 'evolution.allowunstable',
478 478 default=None,
479 479 )
480 480 coreconfigitem('experimental', 'evolution.createmarkers',
481 481 default=None,
482 482 )
483 483 coreconfigitem('experimental', 'evolution.effect-flags',
484 484 default=True,
485 485 alias=[('experimental', 'effect-flags')]
486 486 )
487 487 coreconfigitem('experimental', 'evolution.exchange',
488 488 default=None,
489 489 )
490 490 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
491 491 default=False,
492 492 )
493 493 coreconfigitem('experimental', 'evolution.report-instabilities',
494 494 default=True,
495 495 )
496 496 coreconfigitem('experimental', 'evolution.track-operation',
497 497 default=True,
498 498 )
499 499 coreconfigitem('experimental', 'worddiff',
500 500 default=False,
501 501 )
502 502 coreconfigitem('experimental', 'maxdeltachainspan',
503 503 default=-1,
504 504 )
505 505 coreconfigitem('experimental', 'mmapindexthreshold',
506 506 default=None,
507 507 )
508 508 coreconfigitem('experimental', 'nonnormalparanoidcheck',
509 509 default=False,
510 510 )
511 511 coreconfigitem('experimental', 'exportableenviron',
512 512 default=list,
513 513 )
514 514 coreconfigitem('experimental', 'extendedheader.index',
515 515 default=None,
516 516 )
517 517 coreconfigitem('experimental', 'extendedheader.similarity',
518 518 default=False,
519 519 )
520 520 coreconfigitem('experimental', 'format.compression',
521 521 default='zlib',
522 522 )
523 523 coreconfigitem('experimental', 'graphshorten',
524 524 default=False,
525 525 )
526 526 coreconfigitem('experimental', 'graphstyle.parent',
527 527 default=dynamicdefault,
528 528 )
529 529 coreconfigitem('experimental', 'graphstyle.missing',
530 530 default=dynamicdefault,
531 531 )
532 532 coreconfigitem('experimental', 'graphstyle.grandparent',
533 533 default=dynamicdefault,
534 534 )
535 535 coreconfigitem('experimental', 'hook-track-tags',
536 536 default=False,
537 537 )
538 538 coreconfigitem('experimental', 'httppostargs',
539 539 default=False,
540 540 )
541 541 coreconfigitem('experimental', 'manifestv2',
542 542 default=False,
543 543 )
544 544 coreconfigitem('experimental', 'mergedriver',
545 545 default=None,
546 546 )
547 547 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
548 548 default=False,
549 549 )
550 550 coreconfigitem('experimental', 'remotenames',
551 551 default=False,
552 552 )
553 553 coreconfigitem('experimental', 'revlogv2',
554 554 default=None,
555 555 )
556 556 coreconfigitem('experimental', 'single-head-per-branch',
557 557 default=False,
558 558 )
559 559 coreconfigitem('experimental', 'spacemovesdown',
560 560 default=False,
561 561 )
562 562 coreconfigitem('experimental', 'sparse-read',
563 563 default=False,
564 564 )
565 565 coreconfigitem('experimental', 'sparse-read.density-threshold',
566 566 default=0.25,
567 567 )
568 568 coreconfigitem('experimental', 'sparse-read.min-gap-size',
569 569 default='256K',
570 570 )
571 571 coreconfigitem('experimental', 'treemanifest',
572 572 default=False,
573 573 )
574 574 coreconfigitem('experimental', 'update.atomic-file',
575 575 default=False,
576 576 )
577 coreconfigitem('experimental', 'sshpeer.advertise-v2',
578 default=False,
579 )
577 580 coreconfigitem('extensions', '.*',
578 581 default=None,
579 582 generic=True,
580 583 )
581 584 coreconfigitem('extdata', '.*',
582 585 default=None,
583 586 generic=True,
584 587 )
585 588 coreconfigitem('format', 'aggressivemergedeltas',
586 589 default=False,
587 590 )
588 591 coreconfigitem('format', 'chunkcachesize',
589 592 default=None,
590 593 )
591 594 coreconfigitem('format', 'dotencode',
592 595 default=True,
593 596 )
594 597 coreconfigitem('format', 'generaldelta',
595 598 default=False,
596 599 )
597 600 coreconfigitem('format', 'manifestcachesize',
598 601 default=None,
599 602 )
600 603 coreconfigitem('format', 'maxchainlen',
601 604 default=None,
602 605 )
603 606 coreconfigitem('format', 'obsstore-version',
604 607 default=None,
605 608 )
606 609 coreconfigitem('format', 'usefncache',
607 610 default=True,
608 611 )
609 612 coreconfigitem('format', 'usegeneraldelta',
610 613 default=True,
611 614 )
612 615 coreconfigitem('format', 'usestore',
613 616 default=True,
614 617 )
615 618 coreconfigitem('fsmonitor', 'warn_when_unused',
616 619 default=True,
617 620 )
618 621 coreconfigitem('fsmonitor', 'warn_update_file_count',
619 622 default=50000,
620 623 )
621 624 coreconfigitem('hooks', '.*',
622 625 default=dynamicdefault,
623 626 generic=True,
624 627 )
625 628 coreconfigitem('hgweb-paths', '.*',
626 629 default=list,
627 630 generic=True,
628 631 )
629 632 coreconfigitem('hostfingerprints', '.*',
630 633 default=list,
631 634 generic=True,
632 635 )
633 636 coreconfigitem('hostsecurity', 'ciphers',
634 637 default=None,
635 638 )
636 639 coreconfigitem('hostsecurity', 'disabletls10warning',
637 640 default=False,
638 641 )
639 642 coreconfigitem('hostsecurity', 'minimumprotocol',
640 643 default=dynamicdefault,
641 644 )
642 645 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
643 646 default=dynamicdefault,
644 647 generic=True,
645 648 )
646 649 coreconfigitem('hostsecurity', '.*:ciphers$',
647 650 default=dynamicdefault,
648 651 generic=True,
649 652 )
650 653 coreconfigitem('hostsecurity', '.*:fingerprints$',
651 654 default=list,
652 655 generic=True,
653 656 )
654 657 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
655 658 default=None,
656 659 generic=True,
657 660 )
658 661
659 662 coreconfigitem('http_proxy', 'always',
660 663 default=False,
661 664 )
662 665 coreconfigitem('http_proxy', 'host',
663 666 default=None,
664 667 )
665 668 coreconfigitem('http_proxy', 'no',
666 669 default=list,
667 670 )
668 671 coreconfigitem('http_proxy', 'passwd',
669 672 default=None,
670 673 )
671 674 coreconfigitem('http_proxy', 'user',
672 675 default=None,
673 676 )
674 677 coreconfigitem('logtoprocess', 'commandexception',
675 678 default=None,
676 679 )
677 680 coreconfigitem('logtoprocess', 'commandfinish',
678 681 default=None,
679 682 )
680 683 coreconfigitem('logtoprocess', 'command',
681 684 default=None,
682 685 )
683 686 coreconfigitem('logtoprocess', 'develwarn',
684 687 default=None,
685 688 )
686 689 coreconfigitem('logtoprocess', 'uiblocked',
687 690 default=None,
688 691 )
689 692 coreconfigitem('merge', 'checkunknown',
690 693 default='abort',
691 694 )
692 695 coreconfigitem('merge', 'checkignored',
693 696 default='abort',
694 697 )
695 698 coreconfigitem('experimental', 'merge.checkpathconflicts',
696 699 default=False,
697 700 )
698 701 coreconfigitem('merge', 'followcopies',
699 702 default=True,
700 703 )
701 704 coreconfigitem('merge', 'on-failure',
702 705 default='continue',
703 706 )
704 707 coreconfigitem('merge', 'preferancestor',
705 708 default=lambda: ['*'],
706 709 )
707 710 coreconfigitem('merge-tools', '.*',
708 711 default=None,
709 712 generic=True,
710 713 )
711 714 coreconfigitem('merge-tools', br'.*\.args$',
712 715 default="$local $base $other",
713 716 generic=True,
714 717 priority=-1,
715 718 )
716 719 coreconfigitem('merge-tools', br'.*\.binary$',
717 720 default=False,
718 721 generic=True,
719 722 priority=-1,
720 723 )
721 724 coreconfigitem('merge-tools', br'.*\.check$',
722 725 default=list,
723 726 generic=True,
724 727 priority=-1,
725 728 )
726 729 coreconfigitem('merge-tools', br'.*\.checkchanged$',
727 730 default=False,
728 731 generic=True,
729 732 priority=-1,
730 733 )
731 734 coreconfigitem('merge-tools', br'.*\.executable$',
732 735 default=dynamicdefault,
733 736 generic=True,
734 737 priority=-1,
735 738 )
736 739 coreconfigitem('merge-tools', br'.*\.fixeol$',
737 740 default=False,
738 741 generic=True,
739 742 priority=-1,
740 743 )
741 744 coreconfigitem('merge-tools', br'.*\.gui$',
742 745 default=False,
743 746 generic=True,
744 747 priority=-1,
745 748 )
746 749 coreconfigitem('merge-tools', br'.*\.mergemarkers$',
747 750 default='basic',
748 751 generic=True,
749 752 priority=-1,
750 753 )
751 754 coreconfigitem('merge-tools', br'.*\.mergemarkertemplate$',
752 755 default=dynamicdefault, # take from ui.mergemarkertemplate
753 756 generic=True,
754 757 priority=-1,
755 758 )
756 759 coreconfigitem('merge-tools', br'.*\.priority$',
757 760 default=0,
758 761 generic=True,
759 762 priority=-1,
760 763 )
761 764 coreconfigitem('merge-tools', br'.*\.premerge$',
762 765 default=dynamicdefault,
763 766 generic=True,
764 767 priority=-1,
765 768 )
766 769 coreconfigitem('merge-tools', br'.*\.symlink$',
767 770 default=False,
768 771 generic=True,
769 772 priority=-1,
770 773 )
771 774 coreconfigitem('pager', 'attend-.*',
772 775 default=dynamicdefault,
773 776 generic=True,
774 777 )
775 778 coreconfigitem('pager', 'ignore',
776 779 default=list,
777 780 )
778 781 coreconfigitem('pager', 'pager',
779 782 default=dynamicdefault,
780 783 )
781 784 coreconfigitem('patch', 'eol',
782 785 default='strict',
783 786 )
784 787 coreconfigitem('patch', 'fuzz',
785 788 default=2,
786 789 )
787 790 coreconfigitem('paths', 'default',
788 791 default=None,
789 792 )
790 793 coreconfigitem('paths', 'default-push',
791 794 default=None,
792 795 )
793 796 coreconfigitem('paths', '.*',
794 797 default=None,
795 798 generic=True,
796 799 )
797 800 coreconfigitem('phases', 'checksubrepos',
798 801 default='follow',
799 802 )
800 803 coreconfigitem('phases', 'new-commit',
801 804 default='draft',
802 805 )
803 806 coreconfigitem('phases', 'publish',
804 807 default=True,
805 808 )
806 809 coreconfigitem('profiling', 'enabled',
807 810 default=False,
808 811 )
809 812 coreconfigitem('profiling', 'format',
810 813 default='text',
811 814 )
812 815 coreconfigitem('profiling', 'freq',
813 816 default=1000,
814 817 )
815 818 coreconfigitem('profiling', 'limit',
816 819 default=30,
817 820 )
818 821 coreconfigitem('profiling', 'nested',
819 822 default=0,
820 823 )
821 824 coreconfigitem('profiling', 'output',
822 825 default=None,
823 826 )
824 827 coreconfigitem('profiling', 'showmax',
825 828 default=0.999,
826 829 )
827 830 coreconfigitem('profiling', 'showmin',
828 831 default=dynamicdefault,
829 832 )
830 833 coreconfigitem('profiling', 'sort',
831 834 default='inlinetime',
832 835 )
833 836 coreconfigitem('profiling', 'statformat',
834 837 default='hotpath',
835 838 )
836 839 coreconfigitem('profiling', 'type',
837 840 default='stat',
838 841 )
839 842 coreconfigitem('progress', 'assume-tty',
840 843 default=False,
841 844 )
842 845 coreconfigitem('progress', 'changedelay',
843 846 default=1,
844 847 )
845 848 coreconfigitem('progress', 'clear-complete',
846 849 default=True,
847 850 )
848 851 coreconfigitem('progress', 'debug',
849 852 default=False,
850 853 )
851 854 coreconfigitem('progress', 'delay',
852 855 default=3,
853 856 )
854 857 coreconfigitem('progress', 'disable',
855 858 default=False,
856 859 )
857 860 coreconfigitem('progress', 'estimateinterval',
858 861 default=60.0,
859 862 )
860 863 coreconfigitem('progress', 'format',
861 864 default=lambda: ['topic', 'bar', 'number', 'estimate'],
862 865 )
863 866 coreconfigitem('progress', 'refresh',
864 867 default=0.1,
865 868 )
866 869 coreconfigitem('progress', 'width',
867 870 default=dynamicdefault,
868 871 )
869 872 coreconfigitem('push', 'pushvars.server',
870 873 default=False,
871 874 )
872 875 coreconfigitem('server', 'bookmarks-pushkey-compat',
873 876 default=True,
874 877 )
875 878 coreconfigitem('server', 'bundle1',
876 879 default=True,
877 880 )
878 881 coreconfigitem('server', 'bundle1gd',
879 882 default=None,
880 883 )
881 884 coreconfigitem('server', 'bundle1.pull',
882 885 default=None,
883 886 )
884 887 coreconfigitem('server', 'bundle1gd.pull',
885 888 default=None,
886 889 )
887 890 coreconfigitem('server', 'bundle1.push',
888 891 default=None,
889 892 )
890 893 coreconfigitem('server', 'bundle1gd.push',
891 894 default=None,
892 895 )
893 896 coreconfigitem('server', 'compressionengines',
894 897 default=list,
895 898 )
896 899 coreconfigitem('server', 'concurrent-push-mode',
897 900 default='strict',
898 901 )
899 902 coreconfigitem('server', 'disablefullbundle',
900 903 default=False,
901 904 )
902 905 coreconfigitem('server', 'maxhttpheaderlen',
903 906 default=1024,
904 907 )
905 908 coreconfigitem('server', 'preferuncompressed',
906 909 default=False,
907 910 )
908 911 coreconfigitem('server', 'uncompressed',
909 912 default=True,
910 913 )
911 914 coreconfigitem('server', 'uncompressedallowsecret',
912 915 default=False,
913 916 )
914 917 coreconfigitem('server', 'validate',
915 918 default=False,
916 919 )
917 920 coreconfigitem('server', 'zliblevel',
918 921 default=-1,
919 922 )
920 923 coreconfigitem('share', 'pool',
921 924 default=None,
922 925 )
923 926 coreconfigitem('share', 'poolnaming',
924 927 default='identity',
925 928 )
926 929 coreconfigitem('smtp', 'host',
927 930 default=None,
928 931 )
929 932 coreconfigitem('smtp', 'local_hostname',
930 933 default=None,
931 934 )
932 935 coreconfigitem('smtp', 'password',
933 936 default=None,
934 937 )
935 938 coreconfigitem('smtp', 'port',
936 939 default=dynamicdefault,
937 940 )
938 941 coreconfigitem('smtp', 'tls',
939 942 default='none',
940 943 )
941 944 coreconfigitem('smtp', 'username',
942 945 default=None,
943 946 )
944 947 coreconfigitem('sparse', 'missingwarning',
945 948 default=True,
946 949 )
947 950 coreconfigitem('subrepos', 'allowed',
948 951 default=dynamicdefault, # to make backporting simpler
949 952 )
950 953 coreconfigitem('subrepos', 'hg:allowed',
951 954 default=dynamicdefault,
952 955 )
953 956 coreconfigitem('subrepos', 'git:allowed',
954 957 default=dynamicdefault,
955 958 )
956 959 coreconfigitem('subrepos', 'svn:allowed',
957 960 default=dynamicdefault,
958 961 )
959 962 coreconfigitem('templates', '.*',
960 963 default=None,
961 964 generic=True,
962 965 )
963 966 coreconfigitem('trusted', 'groups',
964 967 default=list,
965 968 )
966 969 coreconfigitem('trusted', 'users',
967 970 default=list,
968 971 )
969 972 coreconfigitem('ui', '_usedassubrepo',
970 973 default=False,
971 974 )
972 975 coreconfigitem('ui', 'allowemptycommit',
973 976 default=False,
974 977 )
975 978 coreconfigitem('ui', 'archivemeta',
976 979 default=True,
977 980 )
978 981 coreconfigitem('ui', 'askusername',
979 982 default=False,
980 983 )
981 984 coreconfigitem('ui', 'clonebundlefallback',
982 985 default=False,
983 986 )
984 987 coreconfigitem('ui', 'clonebundleprefers',
985 988 default=list,
986 989 )
987 990 coreconfigitem('ui', 'clonebundles',
988 991 default=True,
989 992 )
990 993 coreconfigitem('ui', 'color',
991 994 default='auto',
992 995 )
993 996 coreconfigitem('ui', 'commitsubrepos',
994 997 default=False,
995 998 )
996 999 coreconfigitem('ui', 'debug',
997 1000 default=False,
998 1001 )
999 1002 coreconfigitem('ui', 'debugger',
1000 1003 default=None,
1001 1004 )
1002 1005 coreconfigitem('ui', 'editor',
1003 1006 default=dynamicdefault,
1004 1007 )
1005 1008 coreconfigitem('ui', 'fallbackencoding',
1006 1009 default=None,
1007 1010 )
1008 1011 coreconfigitem('ui', 'forcecwd',
1009 1012 default=None,
1010 1013 )
1011 1014 coreconfigitem('ui', 'forcemerge',
1012 1015 default=None,
1013 1016 )
1014 1017 coreconfigitem('ui', 'formatdebug',
1015 1018 default=False,
1016 1019 )
1017 1020 coreconfigitem('ui', 'formatjson',
1018 1021 default=False,
1019 1022 )
1020 1023 coreconfigitem('ui', 'formatted',
1021 1024 default=None,
1022 1025 )
1023 1026 coreconfigitem('ui', 'graphnodetemplate',
1024 1027 default=None,
1025 1028 )
1026 1029 coreconfigitem('ui', 'http2debuglevel',
1027 1030 default=None,
1028 1031 )
1029 1032 coreconfigitem('ui', 'interactive',
1030 1033 default=None,
1031 1034 )
1032 1035 coreconfigitem('ui', 'interface',
1033 1036 default=None,
1034 1037 )
1035 1038 coreconfigitem('ui', 'interface.chunkselector',
1036 1039 default=None,
1037 1040 )
1038 1041 coreconfigitem('ui', 'logblockedtimes',
1039 1042 default=False,
1040 1043 )
1041 1044 coreconfigitem('ui', 'logtemplate',
1042 1045 default=None,
1043 1046 )
1044 1047 coreconfigitem('ui', 'merge',
1045 1048 default=None,
1046 1049 )
1047 1050 coreconfigitem('ui', 'mergemarkers',
1048 1051 default='basic',
1049 1052 )
1050 1053 coreconfigitem('ui', 'mergemarkertemplate',
1051 1054 default=('{node|short} '
1052 1055 '{ifeq(tags, "tip", "", '
1053 1056 'ifeq(tags, "", "", "{tags} "))}'
1054 1057 '{if(bookmarks, "{bookmarks} ")}'
1055 1058 '{ifeq(branch, "default", "", "{branch} ")}'
1056 1059 '- {author|user}: {desc|firstline}')
1057 1060 )
1058 1061 coreconfigitem('ui', 'nontty',
1059 1062 default=False,
1060 1063 )
1061 1064 coreconfigitem('ui', 'origbackuppath',
1062 1065 default=None,
1063 1066 )
1064 1067 coreconfigitem('ui', 'paginate',
1065 1068 default=True,
1066 1069 )
1067 1070 coreconfigitem('ui', 'patch',
1068 1071 default=None,
1069 1072 )
1070 1073 coreconfigitem('ui', 'portablefilenames',
1071 1074 default='warn',
1072 1075 )
1073 1076 coreconfigitem('ui', 'promptecho',
1074 1077 default=False,
1075 1078 )
1076 1079 coreconfigitem('ui', 'quiet',
1077 1080 default=False,
1078 1081 )
1079 1082 coreconfigitem('ui', 'quietbookmarkmove',
1080 1083 default=False,
1081 1084 )
1082 1085 coreconfigitem('ui', 'remotecmd',
1083 1086 default='hg',
1084 1087 )
1085 1088 coreconfigitem('ui', 'report_untrusted',
1086 1089 default=True,
1087 1090 )
1088 1091 coreconfigitem('ui', 'rollback',
1089 1092 default=True,
1090 1093 )
1091 1094 coreconfigitem('ui', 'slash',
1092 1095 default=False,
1093 1096 )
1094 1097 coreconfigitem('ui', 'ssh',
1095 1098 default='ssh',
1096 1099 )
1097 1100 coreconfigitem('ui', 'ssherrorhint',
1098 1101 default=None,
1099 1102 )
1100 1103 coreconfigitem('ui', 'statuscopies',
1101 1104 default=False,
1102 1105 )
1103 1106 coreconfigitem('ui', 'strict',
1104 1107 default=False,
1105 1108 )
1106 1109 coreconfigitem('ui', 'style',
1107 1110 default='',
1108 1111 )
1109 1112 coreconfigitem('ui', 'supportcontact',
1110 1113 default=None,
1111 1114 )
1112 1115 coreconfigitem('ui', 'textwidth',
1113 1116 default=78,
1114 1117 )
1115 1118 coreconfigitem('ui', 'timeout',
1116 1119 default='600',
1117 1120 )
1118 1121 coreconfigitem('ui', 'timeout.warn',
1119 1122 default=0,
1120 1123 )
1121 1124 coreconfigitem('ui', 'traceback',
1122 1125 default=False,
1123 1126 )
1124 1127 coreconfigitem('ui', 'tweakdefaults',
1125 1128 default=False,
1126 1129 )
1127 1130 coreconfigitem('ui', 'usehttp2',
1128 1131 default=False,
1129 1132 )
1130 1133 coreconfigitem('ui', 'username',
1131 1134 alias=[('ui', 'user')]
1132 1135 )
1133 1136 coreconfigitem('ui', 'verbose',
1134 1137 default=False,
1135 1138 )
1136 1139 coreconfigitem('verify', 'skipflags',
1137 1140 default=None,
1138 1141 )
1139 1142 coreconfigitem('web', 'allowbz2',
1140 1143 default=False,
1141 1144 )
1142 1145 coreconfigitem('web', 'allowgz',
1143 1146 default=False,
1144 1147 )
1145 1148 coreconfigitem('web', 'allow-pull',
1146 1149 alias=[('web', 'allowpull')],
1147 1150 default=True,
1148 1151 )
1149 1152 coreconfigitem('web', 'allow-push',
1150 1153 alias=[('web', 'allow_push')],
1151 1154 default=list,
1152 1155 )
1153 1156 coreconfigitem('web', 'allowzip',
1154 1157 default=False,
1155 1158 )
1156 1159 coreconfigitem('web', 'archivesubrepos',
1157 1160 default=False,
1158 1161 )
1159 1162 coreconfigitem('web', 'cache',
1160 1163 default=True,
1161 1164 )
1162 1165 coreconfigitem('web', 'contact',
1163 1166 default=None,
1164 1167 )
1165 1168 coreconfigitem('web', 'deny_push',
1166 1169 default=list,
1167 1170 )
1168 1171 coreconfigitem('web', 'guessmime',
1169 1172 default=False,
1170 1173 )
1171 1174 coreconfigitem('web', 'hidden',
1172 1175 default=False,
1173 1176 )
1174 1177 coreconfigitem('web', 'labels',
1175 1178 default=list,
1176 1179 )
1177 1180 coreconfigitem('web', 'logoimg',
1178 1181 default='hglogo.png',
1179 1182 )
1180 1183 coreconfigitem('web', 'logourl',
1181 1184 default='https://mercurial-scm.org/',
1182 1185 )
1183 1186 coreconfigitem('web', 'accesslog',
1184 1187 default='-',
1185 1188 )
1186 1189 coreconfigitem('web', 'address',
1187 1190 default='',
1188 1191 )
1189 1192 coreconfigitem('web', 'allow_archive',
1190 1193 default=list,
1191 1194 )
1192 1195 coreconfigitem('web', 'allow_read',
1193 1196 default=list,
1194 1197 )
1195 1198 coreconfigitem('web', 'baseurl',
1196 1199 default=None,
1197 1200 )
1198 1201 coreconfigitem('web', 'cacerts',
1199 1202 default=None,
1200 1203 )
1201 1204 coreconfigitem('web', 'certificate',
1202 1205 default=None,
1203 1206 )
1204 1207 coreconfigitem('web', 'collapse',
1205 1208 default=False,
1206 1209 )
1207 1210 coreconfigitem('web', 'csp',
1208 1211 default=None,
1209 1212 )
1210 1213 coreconfigitem('web', 'deny_read',
1211 1214 default=list,
1212 1215 )
1213 1216 coreconfigitem('web', 'descend',
1214 1217 default=True,
1215 1218 )
1216 1219 coreconfigitem('web', 'description',
1217 1220 default="",
1218 1221 )
1219 1222 coreconfigitem('web', 'encoding',
1220 1223 default=lambda: encoding.encoding,
1221 1224 )
1222 1225 coreconfigitem('web', 'errorlog',
1223 1226 default='-',
1224 1227 )
1225 1228 coreconfigitem('web', 'ipv6',
1226 1229 default=False,
1227 1230 )
1228 1231 coreconfigitem('web', 'maxchanges',
1229 1232 default=10,
1230 1233 )
1231 1234 coreconfigitem('web', 'maxfiles',
1232 1235 default=10,
1233 1236 )
1234 1237 coreconfigitem('web', 'maxshortchanges',
1235 1238 default=60,
1236 1239 )
1237 1240 coreconfigitem('web', 'motd',
1238 1241 default='',
1239 1242 )
1240 1243 coreconfigitem('web', 'name',
1241 1244 default=dynamicdefault,
1242 1245 )
1243 1246 coreconfigitem('web', 'port',
1244 1247 default=8000,
1245 1248 )
1246 1249 coreconfigitem('web', 'prefix',
1247 1250 default='',
1248 1251 )
1249 1252 coreconfigitem('web', 'push_ssl',
1250 1253 default=True,
1251 1254 )
1252 1255 coreconfigitem('web', 'refreshinterval',
1253 1256 default=20,
1254 1257 )
1255 1258 coreconfigitem('web', 'staticurl',
1256 1259 default=None,
1257 1260 )
1258 1261 coreconfigitem('web', 'stripes',
1259 1262 default=1,
1260 1263 )
1261 1264 coreconfigitem('web', 'style',
1262 1265 default='paper',
1263 1266 )
1264 1267 coreconfigitem('web', 'templates',
1265 1268 default=None,
1266 1269 )
1267 1270 coreconfigitem('web', 'view',
1268 1271 default='served',
1269 1272 )
1270 1273 coreconfigitem('worker', 'backgroundclose',
1271 1274 default=dynamicdefault,
1272 1275 )
1273 1276 # Windows defaults to a limit of 512 open files. A buffer of 128
1274 1277 # should give us enough headway.
1275 1278 coreconfigitem('worker', 'backgroundclosemaxqueue',
1276 1279 default=384,
1277 1280 )
1278 1281 coreconfigitem('worker', 'backgroundcloseminfilecount',
1279 1282 default=2048,
1280 1283 )
1281 1284 coreconfigitem('worker', 'backgroundclosethreadcount',
1282 1285 default=4,
1283 1286 )
1284 1287 coreconfigitem('worker', 'enabled',
1285 1288 default=True,
1286 1289 )
1287 1290 coreconfigitem('worker', 'numcpus',
1288 1291 default=None,
1289 1292 )
1290 1293
1291 1294 # Rebase related configuration moved to core because other extension are doing
1292 1295 # strange things. For example, shelve import the extensions to reuse some bit
1293 1296 # without formally loading it.
1294 1297 coreconfigitem('commands', 'rebase.requiredest',
1295 1298 default=False,
1296 1299 )
1297 1300 coreconfigitem('experimental', 'rebaseskipobsolete',
1298 1301 default=True,
1299 1302 )
1300 1303 coreconfigitem('rebase', 'singletransaction',
1301 1304 default=False,
1302 1305 )
1303 1306 coreconfigitem('rebase', 'experimental.inmemory',
1304 1307 default=False,
1305 1308 )
@@ -1,990 +1,1104 b''
1 1 The Mercurial wire protocol is a request-response based protocol
2 2 with multiple wire representations.
3 3
4 4 Each request is modeled as a command name, a dictionary of arguments, and
5 5 optional raw input. Command arguments and their types are intrinsic
6 6 properties of commands. So is the response type of the command. This means
7 7 clients can't always send arbitrary arguments to servers and servers can't
8 8 return multiple response types.
9 9
10 10 The protocol is synchronous and does not support multiplexing (concurrent
11 11 commands).
12 12
13 13 Handshake
14 14 =========
15 15
16 16 It is required or common for clients to perform a *handshake* when connecting
17 17 to a server. The handshake serves the following purposes:
18 18
19 19 * Negotiating protocol/transport level options
20 20 * Allows the client to learn about server capabilities to influence
21 21 future requests
22 22 * Ensures the underlying transport channel is in a *clean* state
23 23
24 24 An important goal of the handshake is to allow clients to use more modern
25 25 wire protocol features. By default, clients must assume they are talking
26 26 to an old version of Mercurial server (possibly even the very first
27 27 implementation). So, clients should not attempt to call or utilize modern
28 28 wire protocol features until they have confirmation that the server
29 29 supports them. The handshake implementation is designed to allow both
30 30 ends to utilize the latest set of features and capabilities with as
31 31 few round trips as possible.
32 32
33 33 The handshake mechanism varies by transport and protocol and is documented
34 34 in the sections below.
35 35
36 36 HTTP Protocol
37 37 =============
38 38
39 39 Handshake
40 40 ---------
41 41
42 42 The client sends a ``capabilities`` command request (``?cmd=capabilities``)
43 43 as soon as HTTP requests may be issued.
44 44
45 45 The server responds with a capabilities string, which the client parses to
46 46 learn about the server's abilities.
47 47
48 48 HTTP Version 1 Transport
49 49 ------------------------
50 50
51 51 Commands are issued as HTTP/1.0 or HTTP/1.1 requests. Commands are
52 52 sent to the base URL of the repository with the command name sent in
53 53 the ``cmd`` query string parameter. e.g.
54 54 ``https://example.com/repo?cmd=capabilities``. The HTTP method is ``GET``
55 55 or ``POST`` depending on the command and whether there is a request
56 56 body.
57 57
58 58 Command arguments can be sent multiple ways.
59 59
60 60 The simplest is part of the URL query string using ``x-www-form-urlencoded``
61 61 encoding (see Python's ``urllib.urlencode()``. However, many servers impose
62 62 length limitations on the URL. So this mechanism is typically only used if
63 63 the server doesn't support other mechanisms.
64 64
65 65 If the server supports the ``httpheader`` capability, command arguments can
66 66 be sent in HTTP request headers named ``X-HgArg-<N>`` where ``<N>`` is an
67 67 integer starting at 1. A ``x-www-form-urlencoded`` representation of the
68 68 arguments is obtained. This full string is then split into chunks and sent
69 69 in numbered ``X-HgArg-<N>`` headers. The maximum length of each HTTP header
70 70 is defined by the server in the ``httpheader`` capability value, which defaults
71 71 to ``1024``. The server reassembles the encoded arguments string by
72 72 concatenating the ``X-HgArg-<N>`` headers then URL decodes them into a
73 73 dictionary.
74 74
75 75 The list of ``X-HgArg-<N>`` headers should be added to the ``Vary`` request
76 76 header to instruct caches to take these headers into consideration when caching
77 77 requests.
78 78
79 79 If the server supports the ``httppostargs`` capability, the client
80 80 may send command arguments in the HTTP request body as part of an
81 81 HTTP POST request. The command arguments will be URL encoded just like
82 82 they would for sending them via HTTP headers. However, no splitting is
83 83 performed: the raw arguments are included in the HTTP request body.
84 84
85 85 The client sends a ``X-HgArgs-Post`` header with the string length of the
86 86 encoded arguments data. Additional data may be included in the HTTP
87 87 request body immediately following the argument data. The offset of the
88 88 non-argument data is defined by the ``X-HgArgs-Post`` header. The
89 89 ``X-HgArgs-Post`` header is not required if there is no argument data.
90 90
91 91 Additional command data can be sent as part of the HTTP request body. The
92 92 default ``Content-Type`` when sending data is ``application/mercurial-0.1``.
93 93 A ``Content-Length`` header is currently always sent.
94 94
95 95 Example HTTP requests::
96 96
97 97 GET /repo?cmd=capabilities
98 98 X-HgArg-1: foo=bar&baz=hello%20world
99 99
100 100 The request media type should be chosen based on server support. If the
101 101 ``httpmediatype`` server capability is present, the client should send
102 102 the newest mutually supported media type. If this capability is absent,
103 103 the client must assume the server only supports the
104 104 ``application/mercurial-0.1`` media type.
105 105
106 106 The ``Content-Type`` HTTP response header identifies the response as coming
107 107 from Mercurial and can also be used to signal an error has occurred.
108 108
109 109 The ``application/mercurial-*`` media types indicate a generic Mercurial
110 110 data type.
111 111
112 112 The ``application/mercurial-0.1`` media type is raw Mercurial data. It is the
113 113 predecessor of the format below.
114 114
115 115 The ``application/mercurial-0.2`` media type is compression framed Mercurial
116 116 data. The first byte of the payload indicates the length of the compression
117 117 format identifier that follows. Next are N bytes indicating the compression
118 118 format. e.g. ``zlib``. The remaining bytes are compressed according to that
119 119 compression format. The decompressed data behaves the same as with
120 120 ``application/mercurial-0.1``.
121 121
122 122 The ``application/hg-error`` media type indicates a generic error occurred.
123 123 The content of the HTTP response body typically holds text describing the
124 124 error.
125 125
126 126 The ``application/hg-changegroup`` media type indicates a changegroup response
127 127 type.
128 128
129 129 Clients also accept the ``text/plain`` media type. All other media
130 130 types should cause the client to error.
131 131
132 132 Behavior of media types is further described in the ``Content Negotiation``
133 133 section below.
134 134
135 135 Clients should issue a ``User-Agent`` request header that identifies the client.
136 136 The server should not use the ``User-Agent`` for feature detection.
137 137
138 138 A command returning a ``string`` response issues a
139 139 ``application/mercurial-0.*`` media type and the HTTP response body contains
140 140 the raw string value (after compression decoding, if used). A
141 141 ``Content-Length`` header is typically issued, but not required.
142 142
143 143 A command returning a ``stream`` response issues a
144 144 ``application/mercurial-0.*`` media type and the HTTP response is typically
145 145 using *chunked transfer* (``Transfer-Encoding: chunked``).
146 146
147 147 SSH Protocol
148 148 ============
149 149
150 150 Handshake
151 151 ---------
152 152
153 153 For all clients, the handshake consists of the client sending 1 or more
154 154 commands to the server using version 1 of the transport. Servers respond
155 155 to commands they know how to respond to and send an empty response (``0\n``)
156 156 for unknown commands (per standard behavior of version 1 of the transport).
157 157 Clients then typically look for a response to the newest sent command to
158 158 determine which transport version to use and what the available features for
159 159 the connection and server are.
160 160
161 161 Preceding any response from client-issued commands, the server may print
162 162 non-protocol output. It is common for SSH servers to print banners, message
163 163 of the day announcements, etc when clients connect. It is assumed that any
164 164 such *banner* output will precede any Mercurial server output. So clients
165 165 must be prepared to handle server output on initial connect that isn't
166 166 in response to any client-issued command and doesn't conform to Mercurial's
167 167 wire protocol. This *banner* output should only be on stdout. However,
168 168 some servers may send output on stderr.
169 169
170 170 Pre 0.9.1 clients issue a ``between`` command with the ``pairs`` argument
171 171 having the value
172 172 ``0000000000000000000000000000000000000000-0000000000000000000000000000000000000000``.
173 173
174 174 The ``between`` command has been supported since the original Mercurial
175 175 SSH server. Requesting the empty range will return a ``\n`` string response,
176 176 which will be encoded as ``1\n\n`` (value length of ``1`` followed by a newline
177 177 followed by the value, which happens to be a newline).
178 178
179 179 For pre 0.9.1 clients and all servers, the exchange looks like::
180 180
181 181 c: between\n
182 182 c: pairs 81\n
183 183 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
184 184 s: 1\n
185 185 s: \n
186 186
187 187 0.9.1+ clients send a ``hello`` command (with no arguments) before the
188 188 ``between`` command. The response to this command allows clients to
189 189 discover server capabilities and settings.
190 190
191 191 An example exchange between 0.9.1+ clients and a ``hello`` aware server looks
192 192 like::
193 193
194 194 c: hello\n
195 195 c: between\n
196 196 c: pairs 81\n
197 197 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
198 198 s: 324\n
199 199 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
200 200 s: 1\n
201 201 s: \n
202 202
203 203 And a similar scenario but with servers sending a banner on connect::
204 204
205 205 c: hello\n
206 206 c: between\n
207 207 c: pairs 81\n
208 208 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
209 209 s: welcome to the server\n
210 210 s: if you find any issues, email someone@somewhere.com\n
211 211 s: 324\n
212 212 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
213 213 s: 1\n
214 214 s: \n
215 215
216 216 Note that output from the ``hello`` command is terminated by a ``\n``. This is
217 217 part of the response payload and not part of the wire protocol adding a newline
218 218 after responses. In other words, the length of the response contains the
219 219 trailing ``\n``.
220 220
221 Clients supporting version 2 of the SSH transport send a line beginning
222 with ``upgrade`` before the ``hello`` and ``between`` commands. The line
223 (which isn't a well-formed command line because it doesn't consist of a
224 single command name) serves to both communicate the client's intent to
225 switch to transport version 2 (transports are version 1 by default) as
226 well as to advertise the client's transport-level capabilities so the
227 server may satisfy that request immediately.
228
229 The upgrade line has the form:
230
231 upgrade <token> <transport capabilities>
232
233 That is the literal string ``upgrade`` followed by a space, followed by
234 a randomly generated string, followed by a space, followed by a string
235 denoting the client's transport capabilities.
236
237 The token can be anything. However, a random UUID is recommended. (Use
238 of version 4 UUIDs is recommended because version 1 UUIDs can leak the
239 client's MAC address.)
240
241 The transport capabilities string is a URL/percent encoded string
242 containing key-value pairs defining the client's transport-level
243 capabilities. The following capabilities are defined:
244
245 proto
246 A comma-delimited list of transport protocol versions the client
247 supports. e.g. ``ssh-v2``.
248
249 If the server does not recognize the ``upgrade`` line, it should issue
250 an empty response and continue processing the ``hello`` and ``between``
251 commands. Here is an example handshake between a version 2 aware client
252 and a non version 2 aware server:
253
254 c: upgrade 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a proto=ssh-v2
255 c: hello\n
256 c: between\n
257 c: pairs 81\n
258 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
259 s: 0\n
260 s: 324\n
261 s: capabilities: lookup changegroupsubset branchmap pushkey known getbundle ...\n
262 s: 1\n
263 s: \n
264
265 (The initial ``0\n`` line from the server indicates an empty response to
266 the unknown ``upgrade ..`` command/line.)
267
268 If the server recognizes the ``upgrade`` line and is willing to satisfy that
269 upgrade request, it replies to with a payload of the following form:
270
271 upgraded <token> <transport name>\n
272
273 This line is the literal string ``upgraded``, a space, the token that was
274 specified by the client in its ``upgrade ...`` request line, a space, and the
275 name of the transport protocol that was chosen by the server. The transport
276 name MUST match one of the names the client specified in the ``proto`` field
277 of its ``upgrade ...`` request line.
278
279 If a server issues an ``upgraded`` response, it MUST also read and ignore
280 the lines associated with the ``hello`` and ``between`` command requests
281 that were issued by the server. It is assumed that the negotiated transport
282 will respond with equivalent requested information following the transport
283 handshake.
284
285 All data following the ``\n`` terminating the ``upgraded`` line is the
286 domain of the negotiated transport. It is common for the data immediately
287 following to contain additional metadata about the state of the transport and
288 the server. However, this isn't strictly speaking part of the transport
289 handshake and isn't covered by this section.
290
291 Here is an example handshake between a version 2 aware client and a version
292 2 aware server:
293
294 c: upgrade 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a proto=ssh-v2
295 c: hello\n
296 c: between\n
297 c: pairs 81\n
298 c: 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
299 s: upgraded 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a ssh-v2\n
300 s: <additional transport specific data>
301
302 The client-issued token that is echoed in the response provides a more
303 resilient mechanism for differentiating *banner* output from Mercurial
304 output. In version 1, properly formatted banner output could get confused
305 for Mercurial server output. By submitting a randomly generated token
306 that is then present in the response, the client can look for that token
307 in response lines and have reasonable certainty that the line did not
308 originate from a *banner* message.
309
221 310 SSH Version 1 Transport
222 311 -----------------------
223 312
224 313 The SSH transport (version 1) is a custom text-based protocol suitable for
225 314 use over any bi-directional stream transport. It is most commonly used with
226 315 SSH.
227 316
228 317 A SSH transport server can be started with ``hg serve --stdio``. The stdin,
229 318 stderr, and stdout file descriptors of the started process are used to exchange
230 319 data. When Mercurial connects to a remote server over SSH, it actually starts
231 320 a ``hg serve --stdio`` process on the remote server.
232 321
233 322 Commands are issued by sending the command name followed by a trailing newline
234 323 ``\n`` to the server. e.g. ``capabilities\n``.
235 324
236 325 Command arguments are sent in the following format::
237 326
238 327 <argument> <length>\n<value>
239 328
240 329 That is, the argument string name followed by a space followed by the
241 330 integer length of the value (expressed as a string) followed by a newline
242 331 (``\n``) followed by the raw argument value.
243 332
244 333 Dictionary arguments are encoded differently::
245 334
246 335 <argument> <# elements>\n
247 336 <key1> <length1>\n<value1>
248 337 <key2> <length2>\n<value2>
249 338 ...
250 339
251 340 Non-argument data is sent immediately after the final argument value. It is
252 341 encoded in chunks::
253 342
254 343 <length>\n<data>
255 344
256 345 Each command declares a list of supported arguments and their types. If a
257 346 client sends an unknown argument to the server, the server should abort
258 347 immediately. The special argument ``*`` in a command's definition indicates
259 348 that all argument names are allowed.
260 349
261 350 The definition of supported arguments and types is initially made when a
262 351 new command is implemented. The client and server must initially independently
263 352 agree on the arguments and their types. This initial set of arguments can be
264 353 supplemented through the presence of *capabilities* advertised by the server.
265 354
266 355 Each command has a defined expected response type.
267 356
268 357 A ``string`` response type is a length framed value. The response consists of
269 358 the string encoded integer length of a value followed by a newline (``\n``)
270 359 followed by the value. Empty values are allowed (and are represented as
271 360 ``0\n``).
272 361
273 362 A ``stream`` response type consists of raw bytes of data. There is no framing.
274 363
275 364 A generic error response type is also supported. It consists of a an error
276 365 message written to ``stderr`` followed by ``\n-\n``. In addition, ``\n`` is
277 366 written to ``stdout``.
278 367
279 368 If the server receives an unknown command, it will send an empty ``string``
280 369 response.
281 370
282 371 The server terminates if it receives an empty command (a ``\n`` character).
283 372
373 SSH Version 2 Transport
374 -----------------------
375
376 **Experimental**
377
378 Version 2 of the SSH transport behaves identically to version 1 of the SSH
379 transport with the exception of handshake semantics. See above for how
380 version 2 of the SSH transport is negotiated.
381
382 Immediately following the ``upgraded`` line signaling a switch to version
383 2 of the SSH protocol, the server automatically sends additional details
384 about the capabilities of the remote server. This has the form:
385
386 <integer length of value>\n
387 capabilities: ...\n
388
389 e.g.
390
391 s: upgraded 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a ssh-v2\n
392 s: 240\n
393 s: capabilities: known getbundle batch ...\n
394
395 Following capabilities advertisement, the peers communicate using version
396 1 of the SSH transport.
397
284 398 Capabilities
285 399 ============
286 400
287 401 Servers advertise supported wire protocol features. This allows clients to
288 402 probe for server features before blindly calling a command or passing a
289 403 specific argument.
290 404
291 405 The server's features are exposed via a *capabilities* string. This is a
292 406 space-delimited string of tokens/features. Some features are single words
293 407 like ``lookup`` or ``batch``. Others are complicated key-value pairs
294 408 advertising sub-features. e.g. ``httpheader=2048``. When complex, non-word
295 409 values are used, each feature name can define its own encoding of sub-values.
296 410 Comma-delimited and ``x-www-form-urlencoded`` values are common.
297 411
298 412 The following document capabilities defined by the canonical Mercurial server
299 413 implementation.
300 414
301 415 batch
302 416 -----
303 417
304 418 Whether the server supports the ``batch`` command.
305 419
306 420 This capability/command was introduced in Mercurial 1.9 (released July 2011).
307 421
308 422 branchmap
309 423 ---------
310 424
311 425 Whether the server supports the ``branchmap`` command.
312 426
313 427 This capability/command was introduced in Mercurial 1.3 (released July 2009).
314 428
315 429 bundle2-exp
316 430 -----------
317 431
318 432 Precursor to ``bundle2`` capability that was used before bundle2 was a
319 433 stable feature.
320 434
321 435 This capability was introduced in Mercurial 3.0 behind an experimental
322 436 flag. This capability should not be observed in the wild.
323 437
324 438 bundle2
325 439 -------
326 440
327 441 Indicates whether the server supports the ``bundle2`` data exchange format.
328 442
329 443 The value of the capability is a URL quoted, newline (``\n``) delimited
330 444 list of keys or key-value pairs.
331 445
332 446 A key is simply a URL encoded string.
333 447
334 448 A key-value pair is a URL encoded key separated from a URL encoded value by
335 449 an ``=``. If the value is a list, elements are delimited by a ``,`` after
336 450 URL encoding.
337 451
338 452 For example, say we have the values::
339 453
340 454 {'HG20': [], 'changegroup': ['01', '02'], 'digests': ['sha1', 'sha512']}
341 455
342 456 We would first construct a string::
343 457
344 458 HG20\nchangegroup=01,02\ndigests=sha1,sha512
345 459
346 460 We would then URL quote this string::
347 461
348 462 HG20%0Achangegroup%3D01%2C02%0Adigests%3Dsha1%2Csha512
349 463
350 464 This capability was introduced in Mercurial 3.4 (released May 2015).
351 465
352 466 changegroupsubset
353 467 -----------------
354 468
355 469 Whether the server supports the ``changegroupsubset`` command.
356 470
357 471 This capability was introduced in Mercurial 0.9.2 (released December
358 472 2006).
359 473
360 474 This capability was introduced at the same time as the ``lookup``
361 475 capability/command.
362 476
363 477 compression
364 478 -----------
365 479
366 480 Declares support for negotiating compression formats.
367 481
368 482 Presence of this capability indicates the server supports dynamic selection
369 483 of compression formats based on the client request.
370 484
371 485 Servers advertising this capability are required to support the
372 486 ``application/mercurial-0.2`` media type in response to commands returning
373 487 streams. Servers may support this media type on any command.
374 488
375 489 The value of the capability is a comma-delimited list of strings declaring
376 490 supported compression formats. The order of the compression formats is in
377 491 server-preferred order, most preferred first.
378 492
379 493 The identifiers used by the official Mercurial distribution are:
380 494
381 495 bzip2
382 496 bzip2
383 497 none
384 498 uncompressed / raw data
385 499 zlib
386 500 zlib (no gzip header)
387 501 zstd
388 502 zstd
389 503
390 504 This capability was introduced in Mercurial 4.1 (released February 2017).
391 505
392 506 getbundle
393 507 ---------
394 508
395 509 Whether the server supports the ``getbundle`` command.
396 510
397 511 This capability was introduced in Mercurial 1.9 (released July 2011).
398 512
399 513 httpheader
400 514 ----------
401 515
402 516 Whether the server supports receiving command arguments via HTTP request
403 517 headers.
404 518
405 519 The value of the capability is an integer describing the max header
406 520 length that clients should send. Clients should ignore any content after a
407 521 comma in the value, as this is reserved for future use.
408 522
409 523 This capability was introduced in Mercurial 1.9 (released July 2011).
410 524
411 525 httpmediatype
412 526 -------------
413 527
414 528 Indicates which HTTP media types (``Content-Type`` header) the server is
415 529 capable of receiving and sending.
416 530
417 531 The value of the capability is a comma-delimited list of strings identifying
418 532 support for media type and transmission direction. The following strings may
419 533 be present:
420 534
421 535 0.1rx
422 536 Indicates server support for receiving ``application/mercurial-0.1`` media
423 537 types.
424 538
425 539 0.1tx
426 540 Indicates server support for sending ``application/mercurial-0.1`` media
427 541 types.
428 542
429 543 0.2rx
430 544 Indicates server support for receiving ``application/mercurial-0.2`` media
431 545 types.
432 546
433 547 0.2tx
434 548 Indicates server support for sending ``application/mercurial-0.2`` media
435 549 types.
436 550
437 551 minrx=X
438 552 Minimum media type version the server is capable of receiving. Value is a
439 553 string like ``0.2``.
440 554
441 555 This capability can be used by servers to limit connections from legacy
442 556 clients not using the latest supported media type. However, only clients
443 557 with knowledge of this capability will know to consult this value. This
444 558 capability is present so the client may issue a more user-friendly error
445 559 when the server has locked out a legacy client.
446 560
447 561 mintx=X
448 562 Minimum media type version the server is capable of sending. Value is a
449 563 string like ``0.1``.
450 564
451 565 Servers advertising support for the ``application/mercurial-0.2`` media type
452 566 should also advertise the ``compression`` capability.
453 567
454 568 This capability was introduced in Mercurial 4.1 (released February 2017).
455 569
456 570 httppostargs
457 571 ------------
458 572
459 573 **Experimental**
460 574
461 575 Indicates that the server supports and prefers clients send command arguments
462 576 via a HTTP POST request as part of the request body.
463 577
464 578 This capability was introduced in Mercurial 3.8 (released May 2016).
465 579
466 580 known
467 581 -----
468 582
469 583 Whether the server supports the ``known`` command.
470 584
471 585 This capability/command was introduced in Mercurial 1.9 (released July 2011).
472 586
473 587 lookup
474 588 ------
475 589
476 590 Whether the server supports the ``lookup`` command.
477 591
478 592 This capability was introduced in Mercurial 0.9.2 (released December
479 593 2006).
480 594
481 595 This capability was introduced at the same time as the ``changegroupsubset``
482 596 capability/command.
483 597
484 598 pushkey
485 599 -------
486 600
487 601 Whether the server supports the ``pushkey`` and ``listkeys`` commands.
488 602
489 603 This capability was introduced in Mercurial 1.6 (released July 2010).
490 604
491 605 standardbundle
492 606 --------------
493 607
494 608 **Unsupported**
495 609
496 610 This capability was introduced during the Mercurial 0.9.2 development cycle in
497 611 2006. It was never present in a release, as it was replaced by the ``unbundle``
498 612 capability. This capability should not be encountered in the wild.
499 613
500 614 stream-preferred
501 615 ----------------
502 616
503 617 If present the server prefers that clients clone using the streaming clone
504 618 protocol (``hg clone --stream``) rather than the standard
505 619 changegroup/bundle based protocol.
506 620
507 621 This capability was introduced in Mercurial 2.2 (released May 2012).
508 622
509 623 streamreqs
510 624 ----------
511 625
512 626 Indicates whether the server supports *streaming clones* and the *requirements*
513 627 that clients must support to receive it.
514 628
515 629 If present, the server supports the ``stream_out`` command, which transmits
516 630 raw revlogs from the repository instead of changegroups. This provides a faster
517 631 cloning mechanism at the expense of more bandwidth used.
518 632
519 633 The value of this capability is a comma-delimited list of repo format
520 634 *requirements*. These are requirements that impact the reading of data in
521 635 the ``.hg/store`` directory. An example value is
522 636 ``streamreqs=generaldelta,revlogv1`` indicating the server repo requires
523 637 the ``revlogv1`` and ``generaldelta`` requirements.
524 638
525 639 If the only format requirement is ``revlogv1``, the server may expose the
526 640 ``stream`` capability instead of the ``streamreqs`` capability.
527 641
528 642 This capability was introduced in Mercurial 1.7 (released November 2010).
529 643
530 644 stream
531 645 ------
532 646
533 647 Whether the server supports *streaming clones* from ``revlogv1`` repos.
534 648
535 649 If present, the server supports the ``stream_out`` command, which transmits
536 650 raw revlogs from the repository instead of changegroups. This provides a faster
537 651 cloning mechanism at the expense of more bandwidth used.
538 652
539 653 This capability was introduced in Mercurial 0.9.1 (released July 2006).
540 654
541 655 When initially introduced, the value of the capability was the numeric
542 656 revlog revision. e.g. ``stream=1``. This indicates the changegroup is using
543 657 ``revlogv1``. This simple integer value wasn't powerful enough, so the
544 658 ``streamreqs`` capability was invented to handle cases where the repo
545 659 requirements have more than just ``revlogv1``. Newer servers omit the
546 660 ``=1`` since it was the only value supported and the value of ``1`` can
547 661 be implied by clients.
548 662
549 663 unbundlehash
550 664 ------------
551 665
552 666 Whether the ``unbundle`` commands supports receiving a hash of all the
553 667 heads instead of a list.
554 668
555 669 For more, see the documentation for the ``unbundle`` command.
556 670
557 671 This capability was introduced in Mercurial 1.9 (released July 2011).
558 672
559 673 unbundle
560 674 --------
561 675
562 676 Whether the server supports pushing via the ``unbundle`` command.
563 677
564 678 This capability/command has been present since Mercurial 0.9.1 (released
565 679 July 2006).
566 680
567 681 Mercurial 0.9.2 (released December 2006) added values to the capability
568 682 indicating which bundle types the server supports receiving. This value is a
569 683 comma-delimited list. e.g. ``HG10GZ,HG10BZ,HG10UN``. The order of values
570 684 reflects the priority/preference of that type, where the first value is the
571 685 most preferred type.
572 686
573 687 Content Negotiation
574 688 ===================
575 689
576 690 The wire protocol has some mechanisms to help peers determine what content
577 691 types and encoding the other side will accept. Historically, these mechanisms
578 692 have been built into commands themselves because most commands only send a
579 693 well-defined response type and only certain commands needed to support
580 694 functionality like compression.
581 695
582 696 Currently, only the HTTP version 1 transport supports content negotiation
583 697 at the protocol layer.
584 698
585 699 HTTP requests advertise supported response formats via the ``X-HgProto-<N>``
586 700 request header, where ``<N>`` is an integer starting at 1 allowing the logical
587 701 value to span multiple headers. This value consists of a list of
588 702 space-delimited parameters. Each parameter denotes a feature or capability.
589 703
590 704 The following parameters are defined:
591 705
592 706 0.1
593 707 Indicates the client supports receiving ``application/mercurial-0.1``
594 708 responses.
595 709
596 710 0.2
597 711 Indicates the client supports receiving ``application/mercurial-0.2``
598 712 responses.
599 713
600 714 comp
601 715 Indicates compression formats the client can decode. Value is a list of
602 716 comma delimited strings identifying compression formats ordered from
603 717 most preferential to least preferential. e.g. ``comp=zstd,zlib,none``.
604 718
605 719 This parameter does not have an effect if only the ``0.1`` parameter
606 720 is defined, as support for ``application/mercurial-0.2`` or greater is
607 721 required to use arbitrary compression formats.
608 722
609 723 If this parameter is not advertised, the server interprets this as
610 724 equivalent to ``zlib,none``.
611 725
612 726 Clients may choose to only send this header if the ``httpmediatype``
613 727 server capability is present, as currently all server-side features
614 728 consulting this header require the client to opt in to new protocol features
615 729 advertised via the ``httpmediatype`` capability.
616 730
617 731 A server that doesn't receive an ``X-HgProto-<N>`` header should infer a
618 732 value of ``0.1``. This is compatible with legacy clients.
619 733
620 734 A server receiving a request indicating support for multiple media type
621 735 versions may respond with any of the supported media types. Not all servers
622 736 may support all media types on all commands.
623 737
624 738 Commands
625 739 ========
626 740
627 741 This section contains a list of all wire protocol commands implemented by
628 742 the canonical Mercurial server.
629 743
630 744 batch
631 745 -----
632 746
633 747 Issue multiple commands while sending a single command request. The purpose
634 748 of this command is to allow a client to issue multiple commands while avoiding
635 749 multiple round trips to the server therefore enabling commands to complete
636 750 quicker.
637 751
638 752 The command accepts a ``cmds`` argument that contains a list of commands to
639 753 execute.
640 754
641 755 The value of ``cmds`` is a ``;`` delimited list of strings. Each string has the
642 756 form ``<command> <arguments>``. That is, the command name followed by a space
643 757 followed by an argument string.
644 758
645 759 The argument string is a ``,`` delimited list of ``<key>=<value>`` values
646 760 corresponding to command arguments. Both the argument name and value are
647 761 escaped using a special substitution map::
648 762
649 763 : -> :c
650 764 , -> :o
651 765 ; -> :s
652 766 = -> :e
653 767
654 768 The response type for this command is ``string``. The value contains a
655 769 ``;`` delimited list of responses for each requested command. Each value
656 770 in this list is escaped using the same substitution map used for arguments.
657 771
658 772 If an error occurs, the generic error response may be sent.
659 773
660 774 between
661 775 -------
662 776
663 777 (Legacy command used for discovery in old clients)
664 778
665 779 Obtain nodes between pairs of nodes.
666 780
667 781 The ``pairs`` arguments contains a space-delimited list of ``-`` delimited
668 782 hex node pairs. e.g.::
669 783
670 784 a072279d3f7fd3a4aa7ffa1a5af8efc573e1c896-6dc58916e7c070f678682bfe404d2e2d68291a18
671 785
672 786 Return type is a ``string``. Value consists of lines corresponding to each
673 787 requested range. Each line contains a space-delimited list of hex nodes.
674 788 A newline ``\n`` terminates each line, including the last one.
675 789
676 790 branchmap
677 791 ---------
678 792
679 793 Obtain heads in named branches.
680 794
681 795 Accepts no arguments. Return type is a ``string``.
682 796
683 797 Return value contains lines with URL encoded branch names followed by a space
684 798 followed by a space-delimited list of hex nodes of heads on that branch.
685 799 e.g.::
686 800
687 801 default a072279d3f7fd3a4aa7ffa1a5af8efc573e1c896 6dc58916e7c070f678682bfe404d2e2d68291a18
688 802 stable baae3bf31522f41dd5e6d7377d0edd8d1cf3fccc
689 803
690 804 There is no trailing newline.
691 805
692 806 branches
693 807 --------
694 808
695 809 (Legacy command used for discovery in old clients. Clients with ``getbundle``
696 810 use the ``known`` and ``heads`` commands instead.)
697 811
698 812 Obtain ancestor changesets of specific nodes back to a branch point.
699 813
700 814 Despite the name, this command has nothing to do with Mercurial named branches.
701 815 Instead, it is related to DAG branches.
702 816
703 817 The command accepts a ``nodes`` argument, which is a string of space-delimited
704 818 hex nodes.
705 819
706 820 For each node requested, the server will find the first ancestor node that is
707 821 a DAG root or is a merge.
708 822
709 823 Return type is a ``string``. Return value contains lines with result data for
710 824 each requested node. Each line contains space-delimited nodes followed by a
711 825 newline (``\n``). The 4 nodes reported on each line correspond to the requested
712 826 node, the ancestor node found, and its 2 parent nodes (which may be the null
713 827 node).
714 828
715 829 capabilities
716 830 ------------
717 831
718 832 Obtain the capabilities string for the repo.
719 833
720 834 Unlike the ``hello`` command, the capabilities string is not prefixed.
721 835 There is no trailing newline.
722 836
723 837 This command does not accept any arguments. Return type is a ``string``.
724 838
725 839 This command was introduced in Mercurial 0.9.1 (released July 2006).
726 840
727 841 changegroup
728 842 -----------
729 843
730 844 (Legacy command: use ``getbundle`` instead)
731 845
732 846 Obtain a changegroup version 1 with data for changesets that are
733 847 descendants of client-specified changesets.
734 848
735 849 The ``roots`` arguments contains a list of space-delimited hex nodes.
736 850
737 851 The server responds with a changegroup version 1 containing all
738 852 changesets between the requested root/base nodes and the repo's head nodes
739 853 at the time of the request.
740 854
741 855 The return type is a ``stream``.
742 856
743 857 changegroupsubset
744 858 -----------------
745 859
746 860 (Legacy command: use ``getbundle`` instead)
747 861
748 862 Obtain a changegroup version 1 with data for changesetsets between
749 863 client specified base and head nodes.
750 864
751 865 The ``bases`` argument contains a list of space-delimited hex nodes.
752 866 The ``heads`` argument contains a list of space-delimited hex nodes.
753 867
754 868 The server responds with a changegroup version 1 containing all
755 869 changesets between the requested base and head nodes at the time of the
756 870 request.
757 871
758 872 The return type is a ``stream``.
759 873
760 874 clonebundles
761 875 ------------
762 876
763 877 Obtains a manifest of bundle URLs available to seed clones.
764 878
765 879 Each returned line contains a URL followed by metadata. See the
766 880 documentation in the ``clonebundles`` extension for more.
767 881
768 882 The return type is a ``string``.
769 883
770 884 getbundle
771 885 ---------
772 886
773 887 Obtain a bundle containing repository data.
774 888
775 889 This command accepts the following arguments:
776 890
777 891 heads
778 892 List of space-delimited hex nodes of heads to retrieve.
779 893 common
780 894 List of space-delimited hex nodes that the client has in common with the
781 895 server.
782 896 obsmarkers
783 897 Boolean indicating whether to include obsolescence markers as part
784 898 of the response. Only works with bundle2.
785 899 bundlecaps
786 900 Comma-delimited set of strings defining client bundle capabilities.
787 901 listkeys
788 902 Comma-delimited list of strings of ``pushkey`` namespaces. For each
789 903 namespace listed, a bundle2 part will be included with the content of
790 904 that namespace.
791 905 cg
792 906 Boolean indicating whether changegroup data is requested.
793 907 cbattempted
794 908 Boolean indicating whether the client attempted to use the *clone bundles*
795 909 feature before performing this request.
796 910 bookmarks
797 911 Boolean indicating whether bookmark data is requested.
798 912 phases
799 913 Boolean indicating whether phases data is requested.
800 914
801 915 The return type on success is a ``stream`` where the value is bundle.
802 916 On the HTTP version 1 transport, the response is zlib compressed.
803 917
804 918 If an error occurs, a generic error response can be sent.
805 919
806 920 Unless the client sends a false value for the ``cg`` argument, the returned
807 921 bundle contains a changegroup with the nodes between the specified ``common``
808 922 and ``heads`` nodes. Depending on the command arguments, the type and content
809 923 of the returned bundle can vary significantly.
810 924
811 925 The default behavior is for the server to send a raw changegroup version
812 926 ``01`` response.
813 927
814 928 If the ``bundlecaps`` provided by the client contain a value beginning
815 929 with ``HG2``, a bundle2 will be returned. The bundle2 data may contain
816 930 additional repository data, such as ``pushkey`` namespace values.
817 931
818 932 heads
819 933 -----
820 934
821 935 Returns a list of space-delimited hex nodes of repository heads followed
822 936 by a newline. e.g.
823 937 ``a9eeb3adc7ddb5006c088e9eda61791c777cbf7c 31f91a3da534dc849f0d6bfc00a395a97cf218a1\n``
824 938
825 939 This command does not accept any arguments. The return type is a ``string``.
826 940
827 941 hello
828 942 -----
829 943
830 944 Returns lines describing interesting things about the server in an RFC-822
831 945 like format.
832 946
833 947 Currently, the only line defines the server capabilities. It has the form::
834 948
835 949 capabilities: <value>
836 950
837 951 See above for more about the capabilities string.
838 952
839 953 SSH clients typically issue this command as soon as a connection is
840 954 established.
841 955
842 956 This command does not accept any arguments. The return type is a ``string``.
843 957
844 958 This command was introduced in Mercurial 0.9.1 (released July 2006).
845 959
846 960 listkeys
847 961 --------
848 962
849 963 List values in a specified ``pushkey`` namespace.
850 964
851 965 The ``namespace`` argument defines the pushkey namespace to operate on.
852 966
853 967 The return type is a ``string``. The value is an encoded dictionary of keys.
854 968
855 969 Key-value pairs are delimited by newlines (``\n``). Within each line, keys and
856 970 values are separated by a tab (``\t``). Keys and values are both strings.
857 971
858 972 lookup
859 973 ------
860 974
861 975 Try to resolve a value to a known repository revision.
862 976
863 977 The ``key`` argument is converted from bytes to an
864 978 ``encoding.localstr`` instance then passed into
865 979 ``localrepository.__getitem__`` in an attempt to resolve it.
866 980
867 981 The return type is a ``string``.
868 982
869 983 Upon successful resolution, returns ``1 <hex node>\n``. On failure,
870 984 returns ``0 <error string>\n``. e.g.::
871 985
872 986 1 273ce12ad8f155317b2c078ec75a4eba507f1fba\n
873 987
874 988 0 unknown revision 'foo'\n
875 989
876 990 known
877 991 -----
878 992
879 993 Determine whether multiple nodes are known.
880 994
881 995 The ``nodes`` argument is a list of space-delimited hex nodes to check
882 996 for existence.
883 997
884 998 The return type is ``string``.
885 999
886 1000 Returns a string consisting of ``0``s and ``1``s indicating whether nodes
887 1001 are known. If the Nth node specified in the ``nodes`` argument is known,
888 1002 a ``1`` will be returned at byte offset N. If the node isn't known, ``0``
889 1003 will be present at byte offset N.
890 1004
891 1005 There is no trailing newline.
892 1006
893 1007 pushkey
894 1008 -------
895 1009
896 1010 Set a value using the ``pushkey`` protocol.
897 1011
898 1012 Accepts arguments ``namespace``, ``key``, ``old``, and ``new``, which
899 1013 correspond to the pushkey namespace to operate on, the key within that
900 1014 namespace to change, the old value (which may be empty), and the new value.
901 1015 All arguments are string types.
902 1016
903 1017 The return type is a ``string``. The value depends on the transport protocol.
904 1018
905 1019 The SSH version 1 transport sends a string encoded integer followed by a
906 1020 newline (``\n``) which indicates operation result. The server may send
907 1021 additional output on the ``stderr`` stream that should be displayed to the
908 1022 user.
909 1023
910 1024 The HTTP version 1 transport sends a string encoded integer followed by a
911 1025 newline followed by additional server output that should be displayed to
912 1026 the user. This may include output from hooks, etc.
913 1027
914 1028 The integer result varies by namespace. ``0`` means an error has occurred
915 1029 and there should be additional output to display to the user.
916 1030
917 1031 stream_out
918 1032 ----------
919 1033
920 1034 Obtain *streaming clone* data.
921 1035
922 1036 The return type is either a ``string`` or a ``stream``, depending on
923 1037 whether the request was fulfilled properly.
924 1038
925 1039 A return value of ``1\n`` indicates the server is not configured to serve
926 1040 this data. If this is seen by the client, they may not have verified the
927 1041 ``stream`` capability is set before making the request.
928 1042
929 1043 A return value of ``2\n`` indicates the server was unable to lock the
930 1044 repository to generate data.
931 1045
932 1046 All other responses are a ``stream`` of bytes. The first line of this data
933 1047 contains 2 space-delimited integers corresponding to the path count and
934 1048 payload size, respectively::
935 1049
936 1050 <path count> <payload size>\n
937 1051
938 1052 The ``<payload size>`` is the total size of path data: it does not include
939 1053 the size of the per-path header lines.
940 1054
941 1055 Following that header are ``<path count>`` entries. Each entry consists of a
942 1056 line with metadata followed by raw revlog data. The line consists of::
943 1057
944 1058 <store path>\0<size>\n
945 1059
946 1060 The ``<store path>`` is the encoded store path of the data that follows.
947 1061 ``<size>`` is the amount of data for this store path/revlog that follows the
948 1062 newline.
949 1063
950 1064 There is no trailer to indicate end of data. Instead, the client should stop
951 1065 reading after ``<path count>`` entries are consumed.
952 1066
953 1067 unbundle
954 1068 --------
955 1069
956 1070 Send a bundle containing data (usually changegroup data) to the server.
957 1071
958 1072 Accepts the argument ``heads``, which is a space-delimited list of hex nodes
959 1073 corresponding to server repository heads observed by the client. This is used
960 1074 to detect race conditions and abort push operations before a server performs
961 1075 too much work or a client transfers too much data.
962 1076
963 1077 The request payload consists of a bundle to be applied to the repository,
964 1078 similarly to as if :hg:`unbundle` were called.
965 1079
966 1080 In most scenarios, a special ``push response`` type is returned. This type
967 1081 contains an integer describing the change in heads as a result of the
968 1082 operation. A value of ``0`` indicates nothing changed. ``1`` means the number
969 1083 of heads remained the same. Values ``2`` and larger indicate the number of
970 1084 added heads minus 1. e.g. ``3`` means 2 heads were added. Negative values
971 1085 indicate the number of fewer heads, also off by 1. e.g. ``-2`` means there
972 1086 is 1 fewer head.
973 1087
974 1088 The encoding of the ``push response`` type varies by transport.
975 1089
976 1090 For the SSH version 1 transport, this type is composed of 2 ``string``
977 1091 responses: an empty response (``0\n``) followed by the integer result value.
978 1092 e.g. ``1\n2``. So the full response might be ``0\n1\n2``.
979 1093
980 1094 For the HTTP version 1 transport, the response is a ``string`` type composed
981 1095 of an integer result value followed by a newline (``\n``) followed by string
982 1096 content holding server output that should be displayed on the client (output
983 1097 hooks, etc).
984 1098
985 1099 In some cases, the server may respond with a ``bundle2`` bundle. In this
986 1100 case, the response type is ``stream``. For the HTTP version 1 transport, the
987 1101 response is zlib compressed.
988 1102
989 1103 The server may also respond with a generic error type, which contains a string
990 1104 indicating the failure.
@@ -1,466 +1,540 b''
1 1 # sshpeer.py - ssh repository proxy class for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import re
11 import uuid
11 12
12 13 from .i18n import _
13 14 from . import (
14 15 error,
15 16 pycompat,
16 17 util,
17 18 wireproto,
19 wireprotoserver,
18 20 )
19 21
20 22 def _serverquote(s):
21 23 """quote a string for the remote shell ... which we assume is sh"""
22 24 if not s:
23 25 return s
24 26 if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s):
25 27 return s
26 28 return "'%s'" % s.replace("'", "'\\''")
27 29
28 30 def _forwardoutput(ui, pipe):
29 31 """display all data currently available on pipe as remote output.
30 32
31 33 This is non blocking."""
32 34 s = util.readpipe(pipe)
33 35 if s:
34 36 for l in s.splitlines():
35 37 ui.status(_("remote: "), l, '\n')
36 38
37 39 class doublepipe(object):
38 40 """Operate a side-channel pipe in addition of a main one
39 41
40 42 The side-channel pipe contains server output to be forwarded to the user
41 43 input. The double pipe will behave as the "main" pipe, but will ensure the
42 44 content of the "side" pipe is properly processed while we wait for blocking
43 45 call on the "main" pipe.
44 46
45 47 If large amounts of data are read from "main", the forward will cease after
46 48 the first bytes start to appear. This simplifies the implementation
47 49 without affecting actual output of sshpeer too much as we rarely issue
48 50 large read for data not yet emitted by the server.
49 51
50 52 The main pipe is expected to be a 'bufferedinputpipe' from the util module
51 53 that handle all the os specific bits. This class lives in this module
52 54 because it focus on behavior specific to the ssh protocol."""
53 55
54 56 def __init__(self, ui, main, side):
55 57 self._ui = ui
56 58 self._main = main
57 59 self._side = side
58 60
59 61 def _wait(self):
60 62 """wait until some data are available on main or side
61 63
62 64 return a pair of boolean (ismainready, issideready)
63 65
64 66 (This will only wait for data if the setup is supported by `util.poll`)
65 67 """
66 68 if getattr(self._main, 'hasbuffer', False): # getattr for classic pipe
67 69 return (True, True) # main has data, assume side is worth poking at.
68 70 fds = [self._main.fileno(), self._side.fileno()]
69 71 try:
70 72 act = util.poll(fds)
71 73 except NotImplementedError:
72 74 # non supported yet case, assume all have data.
73 75 act = fds
74 76 return (self._main.fileno() in act, self._side.fileno() in act)
75 77
76 78 def write(self, data):
77 79 return self._call('write', data)
78 80
79 81 def read(self, size):
80 82 r = self._call('read', size)
81 83 if size != 0 and not r:
82 84 # We've observed a condition that indicates the
83 85 # stdout closed unexpectedly. Check stderr one
84 86 # more time and snag anything that's there before
85 87 # letting anyone know the main part of the pipe
86 88 # closed prematurely.
87 89 _forwardoutput(self._ui, self._side)
88 90 return r
89 91
90 92 def readline(self):
91 93 return self._call('readline')
92 94
93 95 def _call(self, methname, data=None):
94 96 """call <methname> on "main", forward output of "side" while blocking
95 97 """
96 98 # data can be '' or 0
97 99 if (data is not None and not data) or self._main.closed:
98 100 _forwardoutput(self._ui, self._side)
99 101 return ''
100 102 while True:
101 103 mainready, sideready = self._wait()
102 104 if sideready:
103 105 _forwardoutput(self._ui, self._side)
104 106 if mainready:
105 107 meth = getattr(self._main, methname)
106 108 if data is None:
107 109 return meth()
108 110 else:
109 111 return meth(data)
110 112
111 113 def close(self):
112 114 return self._main.close()
113 115
114 116 def flush(self):
115 117 return self._main.flush()
116 118
117 119 def _cleanuppipes(ui, pipei, pipeo, pipee):
118 120 """Clean up pipes used by an SSH connection."""
119 121 if pipeo:
120 122 pipeo.close()
121 123 if pipei:
122 124 pipei.close()
123 125
124 126 if pipee:
125 127 # Try to read from the err descriptor until EOF.
126 128 try:
127 129 for l in pipee:
128 130 ui.status(_('remote: '), l)
129 131 except (IOError, ValueError):
130 132 pass
131 133
132 134 pipee.close()
133 135
134 136 def _makeconnection(ui, sshcmd, args, remotecmd, path, sshenv=None):
135 137 """Create an SSH connection to a server.
136 138
137 139 Returns a tuple of (process, stdin, stdout, stderr) for the
138 140 spawned process.
139 141 """
140 142 cmd = '%s %s %s' % (
141 143 sshcmd,
142 144 args,
143 145 util.shellquote('%s -R %s serve --stdio' % (
144 146 _serverquote(remotecmd), _serverquote(path))))
145 147
146 148 ui.debug('running %s\n' % cmd)
147 149 cmd = util.quotecommand(cmd)
148 150
149 151 # no buffer allow the use of 'select'
150 152 # feel free to remove buffering and select usage when we ultimately
151 153 # move to threading.
152 154 stdin, stdout, stderr, proc = util.popen4(cmd, bufsize=0, env=sshenv)
153 155
154 156 stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr)
155 157 stdin = doublepipe(ui, stdin, stderr)
156 158
157 159 return proc, stdin, stdout, stderr
158 160
159 161 def _performhandshake(ui, stdin, stdout, stderr):
160 162 def badresponse():
161 163 msg = _('no suitable response from remote hg')
162 164 hint = ui.config('ui', 'ssherrorhint')
163 165 raise error.RepoError(msg, hint=hint)
164 166
165 # The handshake consists of sending 2 wire protocol commands:
166 # ``hello`` and ``between``.
167 # The handshake consists of sending wire protocol commands in reverse
168 # order of protocol implementation and then sniffing for a response
169 # to one of them.
170 #
171 # Those commands (from oldest to newest) are:
167 172 #
168 # The ``hello`` command (which was introduced in Mercurial 0.9.1)
169 # instructs the server to advertise its capabilities.
173 # ``between``
174 # Asks for the set of revisions between a pair of revisions. Command
175 # present in all Mercurial server implementations.
170 176 #
171 # The ``between`` command (which has existed in all Mercurial servers
172 # for as long as SSH support has existed), asks for the set of revisions
173 # between a pair of revisions.
177 # ``hello``
178 # Instructs the server to advertise its capabilities. Introduced in
179 # Mercurial 0.9.1.
180 #
181 # ``upgrade``
182 # Requests upgrade from default transport protocol version 1 to
183 # a newer version. Introduced in Mercurial 4.6 as an experimental
184 # feature.
174 185 #
175 186 # The ``between`` command is issued with a request for the null
176 187 # range. If the remote is a Mercurial server, this request will
177 188 # generate a specific response: ``1\n\n``. This represents the
178 189 # wire protocol encoded value for ``\n``. We look for ``1\n\n``
179 190 # in the output stream and know this is the response to ``between``
180 191 # and we're at the end of our handshake reply.
181 192 #
182 193 # The response to the ``hello`` command will be a line with the
183 194 # length of the value returned by that command followed by that
184 195 # value. If the server doesn't support ``hello`` (which should be
185 196 # rare), that line will be ``0\n``. Otherwise, the value will contain
186 197 # RFC 822 like lines. Of these, the ``capabilities:`` line contains
187 198 # the capabilities of the server.
188 199 #
200 # The ``upgrade`` command isn't really a command in the traditional
201 # sense of version 1 of the transport because it isn't using the
202 # proper mechanism for formatting insteads: instead, it just encodes
203 # arguments on the line, delimited by spaces.
204 #
205 # The ``upgrade`` line looks like ``upgrade <token> <capabilities>``.
206 # If the server doesn't support protocol upgrades, it will reply to
207 # this line with ``0\n``. Otherwise, it emits an
208 # ``upgraded <token> <protocol>`` line to both stdout and stderr.
209 # Content immediately following this line describes additional
210 # protocol and server state.
211 #
189 212 # In addition to the responses to our command requests, the server
190 213 # may emit "banner" output on stdout. SSH servers are allowed to
191 214 # print messages to stdout on login. Issuing commands on connection
192 215 # allows us to flush this banner output from the server by scanning
193 216 # for output to our well-known ``between`` command. Of course, if
194 217 # the banner contains ``1\n\n``, this will throw off our detection.
195 218
196 219 requestlog = ui.configbool('devel', 'debug.peer-request')
197 220
221 # Generate a random token to help identify responses to version 2
222 # upgrade request.
223 token = bytes(uuid.uuid4())
224 upgradecaps = [
225 ('proto', wireprotoserver.SSHV2),
226 ]
227 upgradecaps = util.urlreq.urlencode(upgradecaps)
228
198 229 try:
199 230 pairsarg = '%s-%s' % ('0' * 40, '0' * 40)
200 231 handshake = [
201 232 'hello\n',
202 233 'between\n',
203 234 'pairs %d\n' % len(pairsarg),
204 235 pairsarg,
205 236 ]
206 237
238 # Request upgrade to version 2 if configured.
239 if ui.configbool('experimental', 'sshpeer.advertise-v2'):
240 ui.debug('sending upgrade request: %s %s\n' % (token, upgradecaps))
241 handshake.insert(0, 'upgrade %s %s\n' % (token, upgradecaps))
242
207 243 if requestlog:
208 244 ui.debug('devel-peer-request: hello\n')
209 245 ui.debug('sending hello command\n')
210 246 if requestlog:
211 247 ui.debug('devel-peer-request: between\n')
212 248 ui.debug('devel-peer-request: pairs: %d bytes\n' % len(pairsarg))
213 249 ui.debug('sending between command\n')
214 250
215 251 stdin.write(''.join(handshake))
216 252 stdin.flush()
217 253 except IOError:
218 254 badresponse()
219 255
256 # Assume version 1 of wire protocol by default.
257 protoname = wireprotoserver.SSHV1
258 reupgraded = re.compile(b'^upgraded %s (.*)$' % re.escape(token))
259
220 260 lines = ['', 'dummy']
221 261 max_noise = 500
222 262 while lines[-1] and max_noise:
223 263 try:
224 264 l = stdout.readline()
225 265 _forwardoutput(ui, stderr)
266
267 # Look for reply to protocol upgrade request. It has a token
268 # in it, so there should be no false positives.
269 m = reupgraded.match(l)
270 if m:
271 protoname = m.group(1)
272 ui.debug('protocol upgraded to %s\n' % protoname)
273 # If an upgrade was handled, the ``hello`` and ``between``
274 # requests are ignored. The next output belongs to the
275 # protocol, so stop scanning lines.
276 break
277
278 # Otherwise it could be a banner, ``0\n`` response if server
279 # doesn't support upgrade.
280
226 281 if lines[-1] == '1\n' and l == '\n':
227 282 break
228 283 if l:
229 284 ui.debug('remote: ', l)
230 285 lines.append(l)
231 286 max_noise -= 1
232 287 except IOError:
233 288 badresponse()
234 289 else:
235 290 badresponse()
236 291
237 292 caps = set()
238 for l in reversed(lines):
239 # Look for response to ``hello`` command. Scan from the back so
240 # we don't misinterpret banner output as the command reply.
241 if l.startswith('capabilities:'):
242 caps.update(l[:-1].split(':')[1].split())
243 break
244 293
245 # Error if we couldn't find a response to ``hello``. This could
246 # mean:
294 # For version 1, we should see a ``capabilities`` line in response to the
295 # ``hello`` command.
296 if protoname == wireprotoserver.SSHV1:
297 for l in reversed(lines):
298 # Look for response to ``hello`` command. Scan from the back so
299 # we don't misinterpret banner output as the command reply.
300 if l.startswith('capabilities:'):
301 caps.update(l[:-1].split(':')[1].split())
302 break
303 elif protoname == wireprotoserver.SSHV2:
304 # We see a line with number of bytes to follow and then a value
305 # looking like ``capabilities: *``.
306 line = stdout.readline()
307 try:
308 valuelen = int(line)
309 except ValueError:
310 badresponse()
311
312 capsline = stdout.read(valuelen)
313 if not capsline.startswith('capabilities: '):
314 badresponse()
315
316 caps.update(capsline.split(':')[1].split())
317 # Trailing newline.
318 stdout.read(1)
319
320 # Error if we couldn't find capabilities, this means:
247 321 #
248 322 # 1. Remote isn't a Mercurial server
249 323 # 2. Remote is a <0.9.1 Mercurial server
250 324 # 3. Remote is a future Mercurial server that dropped ``hello``
251 # support.
325 # and other attempted handshake mechanisms.
252 326 if not caps:
253 327 badresponse()
254 328
255 329 return caps
256 330
257 331 class sshpeer(wireproto.wirepeer):
258 332 def __init__(self, ui, url, proc, stdin, stdout, stderr, caps):
259 333 """Create a peer from an existing SSH connection.
260 334
261 335 ``proc`` is a handle on the underlying SSH process.
262 336 ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio
263 337 pipes for that process.
264 338 ``caps`` is a set of capabilities supported by the remote.
265 339 """
266 340 self._url = url
267 341 self._ui = ui
268 342 # self._subprocess is unused. Keeping a handle on the process
269 343 # holds a reference and prevents it from being garbage collected.
270 344 self._subprocess = proc
271 345 self._pipeo = stdin
272 346 self._pipei = stdout
273 347 self._pipee = stderr
274 348 self._caps = caps
275 349
276 350 # Begin of _basepeer interface.
277 351
278 352 @util.propertycache
279 353 def ui(self):
280 354 return self._ui
281 355
282 356 def url(self):
283 357 return self._url
284 358
285 359 def local(self):
286 360 return None
287 361
288 362 def peer(self):
289 363 return self
290 364
291 365 def canpush(self):
292 366 return True
293 367
294 368 def close(self):
295 369 pass
296 370
297 371 # End of _basepeer interface.
298 372
299 373 # Begin of _basewirecommands interface.
300 374
301 375 def capabilities(self):
302 376 return self._caps
303 377
304 378 # End of _basewirecommands interface.
305 379
306 380 def _readerr(self):
307 381 _forwardoutput(self.ui, self._pipee)
308 382
309 383 def _abort(self, exception):
310 384 self._cleanup()
311 385 raise exception
312 386
313 387 def _cleanup(self):
314 388 _cleanuppipes(self.ui, self._pipei, self._pipeo, self._pipee)
315 389
316 390 __del__ = _cleanup
317 391
318 392 def _submitbatch(self, req):
319 393 rsp = self._callstream("batch", cmds=wireproto.encodebatchcmds(req))
320 394 available = self._getamount()
321 395 # TODO this response parsing is probably suboptimal for large
322 396 # batches with large responses.
323 397 toread = min(available, 1024)
324 398 work = rsp.read(toread)
325 399 available -= toread
326 400 chunk = work
327 401 while chunk:
328 402 while ';' in work:
329 403 one, work = work.split(';', 1)
330 404 yield wireproto.unescapearg(one)
331 405 toread = min(available, 1024)
332 406 chunk = rsp.read(toread)
333 407 available -= toread
334 408 work += chunk
335 409 yield wireproto.unescapearg(work)
336 410
337 411 def _callstream(self, cmd, **args):
338 412 args = pycompat.byteskwargs(args)
339 413 if (self.ui.debugflag
340 414 and self.ui.configbool('devel', 'debug.peer-request')):
341 415 dbg = self.ui.debug
342 416 line = 'devel-peer-request: %s\n'
343 417 dbg(line % cmd)
344 418 for key, value in sorted(args.items()):
345 419 if not isinstance(value, dict):
346 420 dbg(line % ' %s: %d bytes' % (key, len(value)))
347 421 else:
348 422 for dk, dv in sorted(value.items()):
349 423 dbg(line % ' %s-%s: %d' % (key, dk, len(dv)))
350 424 self.ui.debug("sending %s command\n" % cmd)
351 425 self._pipeo.write("%s\n" % cmd)
352 426 _func, names = wireproto.commands[cmd]
353 427 keys = names.split()
354 428 wireargs = {}
355 429 for k in keys:
356 430 if k == '*':
357 431 wireargs['*'] = args
358 432 break
359 433 else:
360 434 wireargs[k] = args[k]
361 435 del args[k]
362 436 for k, v in sorted(wireargs.iteritems()):
363 437 self._pipeo.write("%s %d\n" % (k, len(v)))
364 438 if isinstance(v, dict):
365 439 for dk, dv in v.iteritems():
366 440 self._pipeo.write("%s %d\n" % (dk, len(dv)))
367 441 self._pipeo.write(dv)
368 442 else:
369 443 self._pipeo.write(v)
370 444 self._pipeo.flush()
371 445
372 446 return self._pipei
373 447
374 448 def _callcompressable(self, cmd, **args):
375 449 return self._callstream(cmd, **args)
376 450
377 451 def _call(self, cmd, **args):
378 452 self._callstream(cmd, **args)
379 453 return self._recv()
380 454
381 455 def _callpush(self, cmd, fp, **args):
382 456 r = self._call(cmd, **args)
383 457 if r:
384 458 return '', r
385 459 for d in iter(lambda: fp.read(4096), ''):
386 460 self._send(d)
387 461 self._send("", flush=True)
388 462 r = self._recv()
389 463 if r:
390 464 return '', r
391 465 return self._recv(), ''
392 466
393 467 def _calltwowaystream(self, cmd, fp, **args):
394 468 r = self._call(cmd, **args)
395 469 if r:
396 470 # XXX needs to be made better
397 471 raise error.Abort(_('unexpected remote reply: %s') % r)
398 472 for d in iter(lambda: fp.read(4096), ''):
399 473 self._send(d)
400 474 self._send("", flush=True)
401 475 return self._pipei
402 476
403 477 def _getamount(self):
404 478 l = self._pipei.readline()
405 479 if l == '\n':
406 480 self._readerr()
407 481 msg = _('check previous remote output')
408 482 self._abort(error.OutOfBandError(hint=msg))
409 483 self._readerr()
410 484 try:
411 485 return int(l)
412 486 except ValueError:
413 487 self._abort(error.ResponseError(_("unexpected response:"), l))
414 488
415 489 def _recv(self):
416 490 return self._pipei.read(self._getamount())
417 491
418 492 def _send(self, data, flush=False):
419 493 self._pipeo.write("%d\n" % len(data))
420 494 if data:
421 495 self._pipeo.write(data)
422 496 if flush:
423 497 self._pipeo.flush()
424 498 self._readerr()
425 499
426 500 def instance(ui, path, create):
427 501 """Create an SSH peer.
428 502
429 503 The returned object conforms to the ``wireproto.wirepeer`` interface.
430 504 """
431 505 u = util.url(path, parsequery=False, parsefragment=False)
432 506 if u.scheme != 'ssh' or not u.host or u.path is None:
433 507 raise error.RepoError(_("couldn't parse location %s") % path)
434 508
435 509 util.checksafessh(path)
436 510
437 511 if u.passwd is not None:
438 512 raise error.RepoError(_('password in URL not supported'))
439 513
440 514 sshcmd = ui.config('ui', 'ssh')
441 515 remotecmd = ui.config('ui', 'remotecmd')
442 516 sshaddenv = dict(ui.configitems('sshenv'))
443 517 sshenv = util.shellenviron(sshaddenv)
444 518 remotepath = u.path or '.'
445 519
446 520 args = util.sshargs(sshcmd, u.host, u.user, u.port)
447 521
448 522 if create:
449 523 cmd = '%s %s %s' % (sshcmd, args,
450 524 util.shellquote('%s init %s' %
451 525 (_serverquote(remotecmd), _serverquote(remotepath))))
452 526 ui.debug('running %s\n' % cmd)
453 527 res = ui.system(cmd, blockedtag='sshpeer', environ=sshenv)
454 528 if res != 0:
455 529 raise error.RepoError(_('could not create remote repo'))
456 530
457 531 proc, stdin, stdout, stderr = _makeconnection(ui, sshcmd, args, remotecmd,
458 532 remotepath, sshenv)
459 533
460 534 try:
461 535 caps = _performhandshake(ui, stdin, stdout, stderr)
462 536 except Exception:
463 537 _cleanuppipes(ui, stdout, stdin, stderr)
464 538 raise
465 539
466 540 return sshpeer(ui, path, proc, stdin, stdout, stderr, caps)
@@ -1,357 +1,363 b''
1 1 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 2 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 3 #
4 4 # This software may be used and distributed according to the terms of the
5 5 # GNU General Public License version 2 or any later version.
6 6
7 7 from __future__ import absolute_import
8 8
9 9 import abc
10 10 import cgi
11 11 import struct
12 12 import sys
13 13
14 14 from .i18n import _
15 15 from . import (
16 16 encoding,
17 17 error,
18 18 hook,
19 19 pycompat,
20 20 util,
21 21 wireproto,
22 22 )
23 23
24 24 stringio = util.stringio
25 25
26 26 urlerr = util.urlerr
27 27 urlreq = util.urlreq
28 28
29 29 HTTP_OK = 200
30 30
31 31 HGTYPE = 'application/mercurial-0.1'
32 32 HGTYPE2 = 'application/mercurial-0.2'
33 33 HGERRTYPE = 'application/hg-error'
34 34
35 # Names of the SSH protocol implementations.
36 SSHV1 = 'ssh-v1'
37 # This is advertised over the wire. Incremental the counter at the end
38 # to reflect BC breakages.
39 SSHV2 = 'exp-ssh-v2-0001'
40
35 41 class abstractserverproto(object):
36 42 """abstract class that summarizes the protocol API
37 43
38 44 Used as reference and documentation.
39 45 """
40 46
41 47 __metaclass__ = abc.ABCMeta
42 48
43 49 @abc.abstractproperty
44 50 def name(self):
45 51 """The name of the protocol implementation.
46 52
47 53 Used for uniquely identifying the transport type.
48 54 """
49 55
50 56 @abc.abstractmethod
51 57 def getargs(self, args):
52 58 """return the value for arguments in <args>
53 59
54 60 returns a list of values (same order as <args>)"""
55 61
56 62 @abc.abstractmethod
57 63 def getfile(self, fp):
58 64 """write the whole content of a file into a file like object
59 65
60 66 The file is in the form::
61 67
62 68 (<chunk-size>\n<chunk>)+0\n
63 69
64 70 chunk size is the ascii version of the int.
65 71 """
66 72
67 73 @abc.abstractmethod
68 74 def redirect(self):
69 75 """may setup interception for stdout and stderr
70 76
71 77 See also the `restore` method."""
72 78
73 79 # If the `redirect` function does install interception, the `restore`
74 80 # function MUST be defined. If interception is not used, this function
75 81 # MUST NOT be defined.
76 82 #
77 83 # left commented here on purpose
78 84 #
79 85 #def restore(self):
80 86 # """reinstall previous stdout and stderr and return intercepted stdout
81 87 # """
82 88 # raise NotImplementedError()
83 89
84 90 def decodevaluefromheaders(req, headerprefix):
85 91 """Decode a long value from multiple HTTP request headers.
86 92
87 93 Returns the value as a bytes, not a str.
88 94 """
89 95 chunks = []
90 96 i = 1
91 97 prefix = headerprefix.upper().replace(r'-', r'_')
92 98 while True:
93 99 v = req.env.get(r'HTTP_%s_%d' % (prefix, i))
94 100 if v is None:
95 101 break
96 102 chunks.append(pycompat.bytesurl(v))
97 103 i += 1
98 104
99 105 return ''.join(chunks)
100 106
101 107 class webproto(abstractserverproto):
102 108 def __init__(self, req, ui):
103 109 self._req = req
104 110 self._ui = ui
105 111
106 112 @property
107 113 def name(self):
108 114 return 'http'
109 115
110 116 def getargs(self, args):
111 117 knownargs = self._args()
112 118 data = {}
113 119 keys = args.split()
114 120 for k in keys:
115 121 if k == '*':
116 122 star = {}
117 123 for key in knownargs.keys():
118 124 if key != 'cmd' and key not in keys:
119 125 star[key] = knownargs[key][0]
120 126 data['*'] = star
121 127 else:
122 128 data[k] = knownargs[k][0]
123 129 return [data[k] for k in keys]
124 130
125 131 def _args(self):
126 132 args = util.rapply(pycompat.bytesurl, self._req.form.copy())
127 133 postlen = int(self._req.env.get(r'HTTP_X_HGARGS_POST', 0))
128 134 if postlen:
129 135 args.update(cgi.parse_qs(
130 136 self._req.read(postlen), keep_blank_values=True))
131 137 return args
132 138
133 139 argvalue = decodevaluefromheaders(self._req, r'X-HgArg')
134 140 args.update(cgi.parse_qs(argvalue, keep_blank_values=True))
135 141 return args
136 142
137 143 def getfile(self, fp):
138 144 length = int(self._req.env[r'CONTENT_LENGTH'])
139 145 # If httppostargs is used, we need to read Content-Length
140 146 # minus the amount that was consumed by args.
141 147 length -= int(self._req.env.get(r'HTTP_X_HGARGS_POST', 0))
142 148 for s in util.filechunkiter(self._req, limit=length):
143 149 fp.write(s)
144 150
145 151 def redirect(self):
146 152 self._oldio = self._ui.fout, self._ui.ferr
147 153 self._ui.ferr = self._ui.fout = stringio()
148 154
149 155 def restore(self):
150 156 val = self._ui.fout.getvalue()
151 157 self._ui.ferr, self._ui.fout = self._oldio
152 158 return val
153 159
154 160 def _client(self):
155 161 return 'remote:%s:%s:%s' % (
156 162 self._req.env.get('wsgi.url_scheme') or 'http',
157 163 urlreq.quote(self._req.env.get('REMOTE_HOST', '')),
158 164 urlreq.quote(self._req.env.get('REMOTE_USER', '')))
159 165
160 166 def responsetype(self, prefer_uncompressed):
161 167 """Determine the appropriate response type and compression settings.
162 168
163 169 Returns a tuple of (mediatype, compengine, engineopts).
164 170 """
165 171 # Determine the response media type and compression engine based
166 172 # on the request parameters.
167 173 protocaps = decodevaluefromheaders(self._req, r'X-HgProto').split(' ')
168 174
169 175 if '0.2' in protocaps:
170 176 # All clients are expected to support uncompressed data.
171 177 if prefer_uncompressed:
172 178 return HGTYPE2, util._noopengine(), {}
173 179
174 180 # Default as defined by wire protocol spec.
175 181 compformats = ['zlib', 'none']
176 182 for cap in protocaps:
177 183 if cap.startswith('comp='):
178 184 compformats = cap[5:].split(',')
179 185 break
180 186
181 187 # Now find an agreed upon compression format.
182 188 for engine in wireproto.supportedcompengines(self._ui, self,
183 189 util.SERVERROLE):
184 190 if engine.wireprotosupport().name in compformats:
185 191 opts = {}
186 192 level = self._ui.configint('server',
187 193 '%slevel' % engine.name())
188 194 if level is not None:
189 195 opts['level'] = level
190 196
191 197 return HGTYPE2, engine, opts
192 198
193 199 # No mutually supported compression format. Fall back to the
194 200 # legacy protocol.
195 201
196 202 # Don't allow untrusted settings because disabling compression or
197 203 # setting a very high compression level could lead to flooding
198 204 # the server's network or CPU.
199 205 opts = {'level': self._ui.configint('server', 'zliblevel')}
200 206 return HGTYPE, util.compengines['zlib'], opts
201 207
202 208 def iscmd(cmd):
203 209 return cmd in wireproto.commands
204 210
205 211 def callhttp(repo, req, cmd):
206 212 proto = webproto(req, repo.ui)
207 213
208 214 def genversion2(gen, engine, engineopts):
209 215 # application/mercurial-0.2 always sends a payload header
210 216 # identifying the compression engine.
211 217 name = engine.wireprotosupport().name
212 218 assert 0 < len(name) < 256
213 219 yield struct.pack('B', len(name))
214 220 yield name
215 221
216 222 for chunk in gen:
217 223 yield chunk
218 224
219 225 rsp = wireproto.dispatch(repo, proto, cmd)
220 226 if isinstance(rsp, bytes):
221 227 req.respond(HTTP_OK, HGTYPE, body=rsp)
222 228 return []
223 229 elif isinstance(rsp, wireproto.streamres_legacy):
224 230 gen = rsp.gen
225 231 req.respond(HTTP_OK, HGTYPE)
226 232 return gen
227 233 elif isinstance(rsp, wireproto.streamres):
228 234 gen = rsp.gen
229 235
230 236 # This code for compression should not be streamres specific. It
231 237 # is here because we only compress streamres at the moment.
232 238 mediatype, engine, engineopts = proto.responsetype(
233 239 rsp.prefer_uncompressed)
234 240 gen = engine.compressstream(gen, engineopts)
235 241
236 242 if mediatype == HGTYPE2:
237 243 gen = genversion2(gen, engine, engineopts)
238 244
239 245 req.respond(HTTP_OK, mediatype)
240 246 return gen
241 247 elif isinstance(rsp, wireproto.pushres):
242 248 val = proto.restore()
243 249 rsp = '%d\n%s' % (rsp.res, val)
244 250 req.respond(HTTP_OK, HGTYPE, body=rsp)
245 251 return []
246 252 elif isinstance(rsp, wireproto.pusherr):
247 253 # drain the incoming bundle
248 254 req.drain()
249 255 proto.restore()
250 256 rsp = '0\n%s\n' % rsp.res
251 257 req.respond(HTTP_OK, HGTYPE, body=rsp)
252 258 return []
253 259 elif isinstance(rsp, wireproto.ooberror):
254 260 rsp = rsp.message
255 261 req.respond(HTTP_OK, HGERRTYPE, body=rsp)
256 262 return []
257 263 raise error.ProgrammingError('hgweb.protocol internal failure', rsp)
258 264
259 265 class sshserver(abstractserverproto):
260 266 def __init__(self, ui, repo):
261 267 self._ui = ui
262 268 self._repo = repo
263 269 self._fin = ui.fin
264 270 self._fout = ui.fout
265 271
266 272 hook.redirect(True)
267 273 ui.fout = repo.ui.fout = ui.ferr
268 274
269 275 # Prevent insertion/deletion of CRs
270 276 util.setbinary(self._fin)
271 277 util.setbinary(self._fout)
272 278
273 279 @property
274 280 def name(self):
275 281 return 'ssh'
276 282
277 283 def getargs(self, args):
278 284 data = {}
279 285 keys = args.split()
280 286 for n in xrange(len(keys)):
281 287 argline = self._fin.readline()[:-1]
282 288 arg, l = argline.split()
283 289 if arg not in keys:
284 290 raise error.Abort(_("unexpected parameter %r") % arg)
285 291 if arg == '*':
286 292 star = {}
287 293 for k in xrange(int(l)):
288 294 argline = self._fin.readline()[:-1]
289 295 arg, l = argline.split()
290 296 val = self._fin.read(int(l))
291 297 star[arg] = val
292 298 data['*'] = star
293 299 else:
294 300 val = self._fin.read(int(l))
295 301 data[arg] = val
296 302 return [data[k] for k in keys]
297 303
298 304 def getfile(self, fpout):
299 305 self._sendresponse('')
300 306 count = int(self._fin.readline())
301 307 while count:
302 308 fpout.write(self._fin.read(count))
303 309 count = int(self._fin.readline())
304 310
305 311 def redirect(self):
306 312 pass
307 313
308 314 def _sendresponse(self, v):
309 315 self._fout.write("%d\n" % len(v))
310 316 self._fout.write(v)
311 317 self._fout.flush()
312 318
313 319 def _sendstream(self, source):
314 320 write = self._fout.write
315 321 for chunk in source.gen:
316 322 write(chunk)
317 323 self._fout.flush()
318 324
319 325 def _sendpushresponse(self, rsp):
320 326 self._sendresponse('')
321 327 self._sendresponse(str(rsp.res))
322 328
323 329 def _sendpusherror(self, rsp):
324 330 self._sendresponse(rsp.res)
325 331
326 332 def _sendooberror(self, rsp):
327 333 self._ui.ferr.write('%s\n-\n' % rsp.message)
328 334 self._ui.ferr.flush()
329 335 self._fout.write('\n')
330 336 self._fout.flush()
331 337
332 338 def serve_forever(self):
333 339 while self.serve_one():
334 340 pass
335 341 sys.exit(0)
336 342
337 343 _handlers = {
338 344 str: _sendresponse,
339 345 wireproto.streamres: _sendstream,
340 346 wireproto.streamres_legacy: _sendstream,
341 347 wireproto.pushres: _sendpushresponse,
342 348 wireproto.pusherr: _sendpusherror,
343 349 wireproto.ooberror: _sendooberror,
344 350 }
345 351
346 352 def serve_one(self):
347 353 cmd = self._fin.readline()[:-1]
348 354 if cmd and cmd in wireproto.commands:
349 355 rsp = wireproto.dispatch(self._repo, self, cmd)
350 356 self._handlers[rsp.__class__](self, rsp)
351 357 elif cmd:
352 358 self._sendresponse("")
353 359 return cmd != ''
354 360
355 361 def _client(self):
356 362 client = encoding.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
357 363 return 'remote:ssh:' + client
@@ -1,96 +1,127 b''
1 1 # sshprotoext.py - Extension to test behavior of SSH protocol
2 2 #
3 3 # Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 # This extension replaces the SSH server started via `hg serve --stdio`.
9 9 # The server behaves differently depending on environment variables.
10 10
11 11 from __future__ import absolute_import
12 12
13 13 from mercurial import (
14 14 error,
15 15 extensions,
16 16 registrar,
17 17 sshpeer,
18 18 wireproto,
19 19 wireprotoserver,
20 20 )
21 21
22 22 configtable = {}
23 23 configitem = registrar.configitem(configtable)
24 24
25 25 configitem('sshpeer', 'mode', default=None)
26 26 configitem('sshpeer', 'handshake-mode', default=None)
27 27
28 28 class bannerserver(wireprotoserver.sshserver):
29 29 """Server that sends a banner to stdout."""
30 30 def serve_forever(self):
31 31 for i in range(10):
32 32 self._fout.write(b'banner: line %d\n' % i)
33 33
34 34 super(bannerserver, self).serve_forever()
35 35
36 36 class prehelloserver(wireprotoserver.sshserver):
37 37 """Tests behavior when connecting to <0.9.1 servers.
38 38
39 39 The ``hello`` wire protocol command was introduced in Mercurial
40 40 0.9.1. Modern clients send the ``hello`` command when connecting
41 41 to SSH servers. This mock server tests behavior of the handshake
42 42 when ``hello`` is not supported.
43 43 """
44 44 def serve_forever(self):
45 45 l = self._fin.readline()
46 46 assert l == b'hello\n'
47 47 # Respond to unknown commands with an empty reply.
48 48 self._sendresponse(b'')
49 49 l = self._fin.readline()
50 50 assert l == b'between\n'
51 51 rsp = wireproto.dispatch(self._repo, self, b'between')
52 52 self._handlers[rsp.__class__](self, rsp)
53 53
54 54 super(prehelloserver, self).serve_forever()
55 55
56 class upgradev2server(wireprotoserver.sshserver):
57 """Tests behavior for clients that issue upgrade to version 2."""
58 def serve_forever(self):
59 name = wireprotoserver.SSHV2
60 l = self._fin.readline()
61 assert l.startswith(b'upgrade ')
62 token, caps = l[:-1].split(b' ')[1:]
63 assert caps == b'proto=%s' % name
64
65 # Filter hello and between requests.
66 l = self._fin.readline()
67 assert l == b'hello\n'
68 l = self._fin.readline()
69 assert l == b'between\n'
70 l = self._fin.readline()
71 assert l == 'pairs 81\n'
72 self._fin.read(81)
73
74 # Send the upgrade response.
75 self._fout.write(b'upgraded %s %s\n' % (token, name))
76 servercaps = wireproto.capabilities(self._repo, self)
77 rsp = b'capabilities: %s' % servercaps
78 self._fout.write(b'%d\n' % len(rsp))
79 self._fout.write(rsp)
80 self._fout.write(b'\n')
81 self._fout.flush()
82
83 super(upgradev2server, self).serve_forever()
84
56 85 def performhandshake(orig, ui, stdin, stdout, stderr):
57 86 """Wrapped version of sshpeer._performhandshake to send extra commands."""
58 87 mode = ui.config(b'sshpeer', b'handshake-mode')
59 88 if mode == b'pre-no-args':
60 89 ui.debug(b'sending no-args command\n')
61 90 stdin.write(b'no-args\n')
62 91 stdin.flush()
63 92 return orig(ui, stdin, stdout, stderr)
64 93 elif mode == b'pre-multiple-no-args':
65 94 ui.debug(b'sending unknown1 command\n')
66 95 stdin.write(b'unknown1\n')
67 96 ui.debug(b'sending unknown2 command\n')
68 97 stdin.write(b'unknown2\n')
69 98 ui.debug(b'sending unknown3 command\n')
70 99 stdin.write(b'unknown3\n')
71 100 stdin.flush()
72 101 return orig(ui, stdin, stdout, stderr)
73 102 else:
74 103 raise error.ProgrammingError(b'unknown HANDSHAKECOMMANDMODE: %s' %
75 104 mode)
76 105
77 106 def extsetup(ui):
78 107 # It's easier for tests to define the server behavior via environment
79 108 # variables than config options. This is because `hg serve --stdio`
80 109 # has to be invoked with a certain form for security reasons and
81 110 # `dummyssh` can't just add `--config` flags to the command line.
82 111 servermode = ui.environ.get(b'SSHSERVERMODE')
83 112
84 113 if servermode == b'banner':
85 114 wireprotoserver.sshserver = bannerserver
86 115 elif servermode == b'no-hello':
87 116 wireprotoserver.sshserver = prehelloserver
117 elif servermode == b'upgradev2':
118 wireprotoserver.sshserver = upgradev2server
88 119 elif servermode:
89 120 raise error.ProgrammingError(b'unknown server mode: %s' % servermode)
90 121
91 122 peermode = ui.config(b'sshpeer', b'mode')
92 123
93 124 if peermode == b'extra-handshake-commands':
94 125 extensions.wrapfunction(sshpeer, '_performhandshake', performhandshake)
95 126 elif peermode:
96 127 raise error.ProgrammingError(b'unknown peer mode: %s' % peermode)
@@ -1,390 +1,494 b''
1 1 $ cat >> $HGRCPATH << EOF
2 2 > [ui]
3 3 > ssh = $PYTHON "$TESTDIR/dummyssh"
4 4 > [devel]
5 5 > debug.peer-request = true
6 6 > [extensions]
7 7 > sshprotoext = $TESTDIR/sshprotoext.py
8 8 > EOF
9 9
10 10 $ hg init server
11 11 $ cd server
12 12 $ echo 0 > foo
13 13 $ hg -q add foo
14 14 $ hg commit -m initial
15 15 $ cd ..
16 16
17 17 Test a normal behaving server, for sanity
18 18
19 19 $ hg --debug debugpeer ssh://user@dummy/server
20 20 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
21 21 devel-peer-request: hello
22 22 sending hello command
23 23 devel-peer-request: between
24 24 devel-peer-request: pairs: 81 bytes
25 25 sending between command
26 26 remote: 384
27 27 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
28 28 remote: 1
29 29 url: ssh://user@dummy/server
30 30 local: no
31 31 pushable: yes
32 32
33 33 Server should answer the "hello" command in isolation
34 34
35 35 $ hg -R server serve --stdio << EOF
36 36 > hello
37 37 > EOF
38 38 384
39 39 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
40 40
41 41 >=0.9.1 clients send a "hello" + "between" for the null range as part of handshake.
42 42 Server should reply with capabilities and should send "1\n\n" as a successful
43 43 reply with empty response to the "between".
44 44
45 45 $ hg -R server serve --stdio << EOF
46 46 > hello
47 47 > between
48 48 > pairs 81
49 49 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
50 50 > EOF
51 51 384
52 52 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
53 53 1
54 54
55 55
56 56 SSH banner is not printed by default, ignored by clients
57 57
58 58 $ SSHSERVERMODE=banner hg debugpeer ssh://user@dummy/server
59 59 url: ssh://user@dummy/server
60 60 local: no
61 61 pushable: yes
62 62
63 63 --debug will print the banner
64 64
65 65 $ SSHSERVERMODE=banner hg --debug debugpeer ssh://user@dummy/server
66 66 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
67 67 devel-peer-request: hello
68 68 sending hello command
69 69 devel-peer-request: between
70 70 devel-peer-request: pairs: 81 bytes
71 71 sending between command
72 72 remote: banner: line 0
73 73 remote: banner: line 1
74 74 remote: banner: line 2
75 75 remote: banner: line 3
76 76 remote: banner: line 4
77 77 remote: banner: line 5
78 78 remote: banner: line 6
79 79 remote: banner: line 7
80 80 remote: banner: line 8
81 81 remote: banner: line 9
82 82 remote: 384
83 83 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
84 84 remote: 1
85 85 url: ssh://user@dummy/server
86 86 local: no
87 87 pushable: yes
88 88
89 89 And test the banner with the raw protocol
90 90
91 91 $ SSHSERVERMODE=banner hg -R server serve --stdio << EOF
92 92 > hello
93 93 > between
94 94 > pairs 81
95 95 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
96 96 > EOF
97 97 banner: line 0
98 98 banner: line 1
99 99 banner: line 2
100 100 banner: line 3
101 101 banner: line 4
102 102 banner: line 5
103 103 banner: line 6
104 104 banner: line 7
105 105 banner: line 8
106 106 banner: line 9
107 107 384
108 108 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
109 109 1
110 110
111 111
112 112 Connecting to a <0.9.1 server that doesn't support the hello command.
113 113 The client should refuse, as we dropped support for connecting to such
114 114 servers.
115 115
116 116 $ SSHSERVERMODE=no-hello hg --debug debugpeer ssh://user@dummy/server
117 117 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
118 118 devel-peer-request: hello
119 119 sending hello command
120 120 devel-peer-request: between
121 121 devel-peer-request: pairs: 81 bytes
122 122 sending between command
123 123 remote: 0
124 124 remote: 1
125 125 abort: no suitable response from remote hg!
126 126 [255]
127 127
128 128 Sending an unknown command to the server results in an empty response to that command
129 129
130 130 $ hg -R server serve --stdio << EOF
131 131 > pre-hello
132 132 > hello
133 133 > between
134 134 > pairs 81
135 135 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
136 136 > EOF
137 137 0
138 138 384
139 139 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
140 140 1
141 141
142 142
143 143 $ hg --config sshpeer.mode=extra-handshake-commands --config sshpeer.handshake-mode=pre-no-args --debug debugpeer ssh://user@dummy/server
144 144 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
145 145 sending no-args command
146 146 devel-peer-request: hello
147 147 sending hello command
148 148 devel-peer-request: between
149 149 devel-peer-request: pairs: 81 bytes
150 150 sending between command
151 151 remote: 0
152 152 remote: 384
153 153 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
154 154 remote: 1
155 155 url: ssh://user@dummy/server
156 156 local: no
157 157 pushable: yes
158 158
159 159 Send multiple unknown commands before hello
160 160
161 161 $ hg -R server serve --stdio << EOF
162 162 > unknown1
163 163 > unknown2
164 164 > unknown3
165 165 > hello
166 166 > between
167 167 > pairs 81
168 168 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
169 169 > EOF
170 170 0
171 171 0
172 172 0
173 173 384
174 174 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
175 175 1
176 176
177 177
178 178 $ hg --config sshpeer.mode=extra-handshake-commands --config sshpeer.handshake-mode=pre-multiple-no-args --debug debugpeer ssh://user@dummy/server
179 179 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
180 180 sending unknown1 command
181 181 sending unknown2 command
182 182 sending unknown3 command
183 183 devel-peer-request: hello
184 184 sending hello command
185 185 devel-peer-request: between
186 186 devel-peer-request: pairs: 81 bytes
187 187 sending between command
188 188 remote: 0
189 189 remote: 0
190 190 remote: 0
191 191 remote: 384
192 192 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
193 193 remote: 1
194 194 url: ssh://user@dummy/server
195 195 local: no
196 196 pushable: yes
197 197
198 198 Send an unknown command before hello that has arguments
199 199
200 200 $ hg -R server serve --stdio << EOF
201 201 > with-args
202 202 > foo 13
203 203 > value for foo
204 204 > bar 13
205 205 > value for bar
206 206 > hello
207 207 > between
208 208 > pairs 81
209 209 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
210 210 > EOF
211 211 0
212 212 0
213 213 0
214 214 0
215 215 0
216 216 384
217 217 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
218 218 1
219 219
220 220
221 221 Send an unknown command having an argument that looks numeric
222 222
223 223 $ hg -R server serve --stdio << EOF
224 224 > unknown
225 225 > foo 1
226 226 > 0
227 227 > hello
228 228 > between
229 229 > pairs 81
230 230 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
231 231 > EOF
232 232 0
233 233 0
234 234 0
235 235 384
236 236 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
237 237 1
238 238
239 239
240 240 $ hg -R server serve --stdio << EOF
241 241 > unknown
242 242 > foo 1
243 243 > 1
244 244 > hello
245 245 > between
246 246 > pairs 81
247 247 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
248 248 > EOF
249 249 0
250 250 0
251 251 0
252 252 384
253 253 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
254 254 1
255 255
256 256
257 257 When sending a dict argument value, it is serialized to
258 258 "<arg> <item count>" followed by "<key> <len>\n<value>" for each item
259 259 in the dict.
260 260
261 261 Dictionary value for unknown command
262 262
263 263 $ hg -R server serve --stdio << EOF
264 264 > unknown
265 265 > dict 3
266 266 > key1 3
267 267 > foo
268 268 > key2 3
269 269 > bar
270 270 > key3 3
271 271 > baz
272 272 > hello
273 273 > EOF
274 274 0
275 275 0
276 276 0
277 277 0
278 278 0
279 279 0
280 280 0
281 281 0
282 282 384
283 283 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
284 284
285 285 Incomplete dictionary send
286 286
287 287 $ hg -R server serve --stdio << EOF
288 288 > unknown
289 289 > dict 3
290 290 > key1 3
291 291 > foo
292 292 > EOF
293 293 0
294 294 0
295 295 0
296 296 0
297 297
298 298 Incomplete value send
299 299
300 300 $ hg -R server serve --stdio << EOF
301 301 > unknown
302 302 > dict 3
303 303 > key1 3
304 304 > fo
305 305 > EOF
306 306 0
307 307 0
308 308 0
309 309 0
310 310
311 311 Send a command line with spaces
312 312
313 313 $ hg -R server serve --stdio << EOF
314 314 > unknown withspace
315 315 > hello
316 316 > between
317 317 > pairs 81
318 318 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
319 319 > EOF
320 320 0
321 321 384
322 322 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
323 323 1
324 324
325 325
326 326 $ hg -R server serve --stdio << EOF
327 327 > unknown with multiple spaces
328 328 > hello
329 329 > between
330 330 > pairs 81
331 331 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
332 332 > EOF
333 333 0
334 334 384
335 335 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
336 336 1
337 337
338 338
339 339 $ hg -R server serve --stdio << EOF
340 340 > unknown with spaces
341 341 > key 10
342 342 > some value
343 343 > hello
344 344 > between
345 345 > pairs 81
346 346 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
347 347 > EOF
348 348 0
349 349 0
350 350 0
351 351 384
352 352 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
353 353 1
354 354
355 355
356 356 Send an unknown command after the "between"
357 357
358 358 $ hg -R server serve --stdio << EOF
359 359 > hello
360 360 > between
361 361 > pairs 81
362 362 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000unknown
363 363 > EOF
364 364 384
365 365 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
366 366 1
367 367
368 368 0
369 369
370 370 And one with arguments
371 371
372 372 $ hg -R server serve --stdio << EOF
373 373 > hello
374 374 > between
375 375 > pairs 81
376 376 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000unknown
377 377 > foo 5
378 378 > value
379 379 > bar 3
380 380 > baz
381 381 > EOF
382 382 384
383 383 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
384 384 1
385 385
386 386 0
387 387 0
388 388 0
389 389 0
390 390 0
391
392 Send an upgrade request to a server that doesn't support that command
393
394 $ hg -R server serve --stdio << EOF
395 > upgrade 2e82ab3f-9ce3-4b4e-8f8c-6fd1c0e9e23a proto=irrelevant1%2Cirrelevant2
396 > hello
397 > between
398 > pairs 81
399 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
400 > EOF
401 0
402 384
403 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
404 1
405
406
407 $ hg --config experimental.sshpeer.advertise-v2=true --debug debugpeer ssh://user@dummy/server
408 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
409 sending upgrade request: * proto=exp-ssh-v2-0001 (glob)
410 devel-peer-request: hello
411 sending hello command
412 devel-peer-request: between
413 devel-peer-request: pairs: 81 bytes
414 sending between command
415 remote: 0
416 remote: 384
417 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
418 remote: 1
419 url: ssh://user@dummy/server
420 local: no
421 pushable: yes
422
423 Send an upgrade request to a server that supports upgrade
424
425 $ SSHSERVERMODE=upgradev2 hg -R server serve --stdio << EOF
426 > upgrade this-is-some-token proto=exp-ssh-v2-0001
427 > hello
428 > between
429 > pairs 81
430 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
431 > EOF
432 upgraded this-is-some-token exp-ssh-v2-0001
433 383
434 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
435
436 $ SSHSERVERMODE=upgradev2 hg --config experimental.sshpeer.advertise-v2=true --debug debugpeer ssh://user@dummy/server
437 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
438 sending upgrade request: * proto=exp-ssh-v2-0001 (glob)
439 devel-peer-request: hello
440 sending hello command
441 devel-peer-request: between
442 devel-peer-request: pairs: 81 bytes
443 sending between command
444 protocol upgraded to exp-ssh-v2-0001
445 url: ssh://user@dummy/server
446 local: no
447 pushable: yes
448
449 Verify the peer has capabilities
450
451 $ SSHSERVERMODE=upgradev2 hg --config experimental.sshpeer.advertise-v2=true --debug debugcapabilities ssh://user@dummy/server
452 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
453 sending upgrade request: * proto=exp-ssh-v2-0001 (glob)
454 devel-peer-request: hello
455 sending hello command
456 devel-peer-request: between
457 devel-peer-request: pairs: 81 bytes
458 sending between command
459 protocol upgraded to exp-ssh-v2-0001
460 Main capabilities:
461 batch
462 branchmap
463 $USUAL_BUNDLE2_CAPS_SERVER$
464 changegroupsubset
465 getbundle
466 known
467 lookup
468 pushkey
469 streamreqs=generaldelta,revlogv1
470 unbundle=HG10GZ,HG10BZ,HG10UN
471 unbundlehash
472 Bundle2 capabilities:
473 HG20
474 bookmarks
475 changegroup
476 01
477 02
478 digests
479 md5
480 sha1
481 sha512
482 error
483 abort
484 unsupportedcontent
485 pushraced
486 pushkey
487 hgtagsfnodes
488 listkeys
489 phases
490 heads
491 pushkey
492 remote-changegroup
493 http
494 https
General Comments 0
You need to be logged in to leave comments. Login now