##// END OF EJS Templates
wireprotov2: advertise recommended batch size for requests...
Gregory Szorc -
r40208:30f70d11 default
parent child Browse files
Show More
@@ -1,1421 +1,1424 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=dynamicdefault,
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('annotate', 'word-diff',
151 151 default=False,
152 152 )
153 153 coreconfigitem('auth', 'cookiefile',
154 154 default=None,
155 155 )
156 156 # bookmarks.pushing: internal hack for discovery
157 157 coreconfigitem('bookmarks', 'pushing',
158 158 default=list,
159 159 )
160 160 # bundle.mainreporoot: internal hack for bundlerepo
161 161 coreconfigitem('bundle', 'mainreporoot',
162 162 default='',
163 163 )
164 164 coreconfigitem('censor', 'policy',
165 165 default='abort',
166 166 )
167 167 coreconfigitem('chgserver', 'idletimeout',
168 168 default=3600,
169 169 )
170 170 coreconfigitem('chgserver', 'skiphash',
171 171 default=False,
172 172 )
173 173 coreconfigitem('cmdserver', 'log',
174 174 default=None,
175 175 )
176 176 coreconfigitem('color', '.*',
177 177 default=None,
178 178 generic=True,
179 179 )
180 180 coreconfigitem('color', 'mode',
181 181 default='auto',
182 182 )
183 183 coreconfigitem('color', 'pagermode',
184 184 default=dynamicdefault,
185 185 )
186 186 coreconfigitem('commands', 'grep.all-files',
187 187 default=False,
188 188 )
189 189 coreconfigitem('commands', 'resolve.confirm',
190 190 default=False,
191 191 )
192 192 coreconfigitem('commands', 'resolve.explicit-re-merge',
193 193 default=False,
194 194 )
195 195 coreconfigitem('commands', 'resolve.mark-check',
196 196 default='none',
197 197 )
198 198 coreconfigitem('commands', 'show.aliasprefix',
199 199 default=list,
200 200 )
201 201 coreconfigitem('commands', 'status.relative',
202 202 default=False,
203 203 )
204 204 coreconfigitem('commands', 'status.skipstates',
205 205 default=[],
206 206 )
207 207 coreconfigitem('commands', 'status.terse',
208 208 default='',
209 209 )
210 210 coreconfigitem('commands', 'status.verbose',
211 211 default=False,
212 212 )
213 213 coreconfigitem('commands', 'update.check',
214 214 default=None,
215 215 )
216 216 coreconfigitem('commands', 'update.requiredest',
217 217 default=False,
218 218 )
219 219 coreconfigitem('committemplate', '.*',
220 220 default=None,
221 221 generic=True,
222 222 )
223 223 coreconfigitem('convert', 'bzr.saverev',
224 224 default=True,
225 225 )
226 226 coreconfigitem('convert', 'cvsps.cache',
227 227 default=True,
228 228 )
229 229 coreconfigitem('convert', 'cvsps.fuzz',
230 230 default=60,
231 231 )
232 232 coreconfigitem('convert', 'cvsps.logencoding',
233 233 default=None,
234 234 )
235 235 coreconfigitem('convert', 'cvsps.mergefrom',
236 236 default=None,
237 237 )
238 238 coreconfigitem('convert', 'cvsps.mergeto',
239 239 default=None,
240 240 )
241 241 coreconfigitem('convert', 'git.committeractions',
242 242 default=lambda: ['messagedifferent'],
243 243 )
244 244 coreconfigitem('convert', 'git.extrakeys',
245 245 default=list,
246 246 )
247 247 coreconfigitem('convert', 'git.findcopiesharder',
248 248 default=False,
249 249 )
250 250 coreconfigitem('convert', 'git.remoteprefix',
251 251 default='remote',
252 252 )
253 253 coreconfigitem('convert', 'git.renamelimit',
254 254 default=400,
255 255 )
256 256 coreconfigitem('convert', 'git.saverev',
257 257 default=True,
258 258 )
259 259 coreconfigitem('convert', 'git.similarity',
260 260 default=50,
261 261 )
262 262 coreconfigitem('convert', 'git.skipsubmodules',
263 263 default=False,
264 264 )
265 265 coreconfigitem('convert', 'hg.clonebranches',
266 266 default=False,
267 267 )
268 268 coreconfigitem('convert', 'hg.ignoreerrors',
269 269 default=False,
270 270 )
271 271 coreconfigitem('convert', 'hg.revs',
272 272 default=None,
273 273 )
274 274 coreconfigitem('convert', 'hg.saverev',
275 275 default=False,
276 276 )
277 277 coreconfigitem('convert', 'hg.sourcename',
278 278 default=None,
279 279 )
280 280 coreconfigitem('convert', 'hg.startrev',
281 281 default=None,
282 282 )
283 283 coreconfigitem('convert', 'hg.tagsbranch',
284 284 default='default',
285 285 )
286 286 coreconfigitem('convert', 'hg.usebranchnames',
287 287 default=True,
288 288 )
289 289 coreconfigitem('convert', 'ignoreancestorcheck',
290 290 default=False,
291 291 )
292 292 coreconfigitem('convert', 'localtimezone',
293 293 default=False,
294 294 )
295 295 coreconfigitem('convert', 'p4.encoding',
296 296 default=dynamicdefault,
297 297 )
298 298 coreconfigitem('convert', 'p4.startrev',
299 299 default=0,
300 300 )
301 301 coreconfigitem('convert', 'skiptags',
302 302 default=False,
303 303 )
304 304 coreconfigitem('convert', 'svn.debugsvnlog',
305 305 default=True,
306 306 )
307 307 coreconfigitem('convert', 'svn.trunk',
308 308 default=None,
309 309 )
310 310 coreconfigitem('convert', 'svn.tags',
311 311 default=None,
312 312 )
313 313 coreconfigitem('convert', 'svn.branches',
314 314 default=None,
315 315 )
316 316 coreconfigitem('convert', 'svn.startrev',
317 317 default=0,
318 318 )
319 319 coreconfigitem('debug', 'dirstate.delaywrite',
320 320 default=0,
321 321 )
322 322 coreconfigitem('defaults', '.*',
323 323 default=None,
324 324 generic=True,
325 325 )
326 326 coreconfigitem('devel', 'all-warnings',
327 327 default=False,
328 328 )
329 329 coreconfigitem('devel', 'bundle2.debug',
330 330 default=False,
331 331 )
332 332 coreconfigitem('devel', 'cache-vfs',
333 333 default=None,
334 334 )
335 335 coreconfigitem('devel', 'check-locks',
336 336 default=False,
337 337 )
338 338 coreconfigitem('devel', 'check-relroot',
339 339 default=False,
340 340 )
341 341 coreconfigitem('devel', 'default-date',
342 342 default=None,
343 343 )
344 344 coreconfigitem('devel', 'deprec-warn',
345 345 default=False,
346 346 )
347 347 coreconfigitem('devel', 'disableloaddefaultcerts',
348 348 default=False,
349 349 )
350 350 coreconfigitem('devel', 'warn-empty-changegroup',
351 351 default=False,
352 352 )
353 353 coreconfigitem('devel', 'legacy.exchange',
354 354 default=list,
355 355 )
356 356 coreconfigitem('devel', 'servercafile',
357 357 default='',
358 358 )
359 359 coreconfigitem('devel', 'serverexactprotocol',
360 360 default='',
361 361 )
362 362 coreconfigitem('devel', 'serverrequirecert',
363 363 default=False,
364 364 )
365 365 coreconfigitem('devel', 'strip-obsmarkers',
366 366 default=True,
367 367 )
368 368 coreconfigitem('devel', 'warn-config',
369 369 default=None,
370 370 )
371 371 coreconfigitem('devel', 'warn-config-default',
372 372 default=None,
373 373 )
374 374 coreconfigitem('devel', 'user.obsmarker',
375 375 default=None,
376 376 )
377 377 coreconfigitem('devel', 'warn-config-unknown',
378 378 default=None,
379 379 )
380 380 coreconfigitem('devel', 'debug.copies',
381 381 default=False,
382 382 )
383 383 coreconfigitem('devel', 'debug.extensions',
384 384 default=False,
385 385 )
386 386 coreconfigitem('devel', 'debug.peer-request',
387 387 default=False,
388 388 )
389 389 coreconfigitem('diff', 'nodates',
390 390 default=False,
391 391 )
392 392 coreconfigitem('diff', 'showfunc',
393 393 default=False,
394 394 )
395 395 coreconfigitem('diff', 'unified',
396 396 default=None,
397 397 )
398 398 coreconfigitem('diff', 'git',
399 399 default=False,
400 400 )
401 401 coreconfigitem('diff', 'ignorews',
402 402 default=False,
403 403 )
404 404 coreconfigitem('diff', 'ignorewsamount',
405 405 default=False,
406 406 )
407 407 coreconfigitem('diff', 'ignoreblanklines',
408 408 default=False,
409 409 )
410 410 coreconfigitem('diff', 'ignorewseol',
411 411 default=False,
412 412 )
413 413 coreconfigitem('diff', 'nobinary',
414 414 default=False,
415 415 )
416 416 coreconfigitem('diff', 'noprefix',
417 417 default=False,
418 418 )
419 419 coreconfigitem('diff', 'word-diff',
420 420 default=False,
421 421 )
422 422 coreconfigitem('email', 'bcc',
423 423 default=None,
424 424 )
425 425 coreconfigitem('email', 'cc',
426 426 default=None,
427 427 )
428 428 coreconfigitem('email', 'charsets',
429 429 default=list,
430 430 )
431 431 coreconfigitem('email', 'from',
432 432 default=None,
433 433 )
434 434 coreconfigitem('email', 'method',
435 435 default='smtp',
436 436 )
437 437 coreconfigitem('email', 'reply-to',
438 438 default=None,
439 439 )
440 440 coreconfigitem('email', 'to',
441 441 default=None,
442 442 )
443 443 coreconfigitem('experimental', 'archivemetatemplate',
444 444 default=dynamicdefault,
445 445 )
446 446 coreconfigitem('experimental', 'bundle-phases',
447 447 default=False,
448 448 )
449 449 coreconfigitem('experimental', 'bundle2-advertise',
450 450 default=True,
451 451 )
452 452 coreconfigitem('experimental', 'bundle2-output-capture',
453 453 default=False,
454 454 )
455 455 coreconfigitem('experimental', 'bundle2.pushback',
456 456 default=False,
457 457 )
458 458 coreconfigitem('experimental', 'bundle2lazylocking',
459 459 default=False,
460 460 )
461 461 coreconfigitem('experimental', 'bundlecomplevel',
462 462 default=None,
463 463 )
464 464 coreconfigitem('experimental', 'bundlecomplevel.bzip2',
465 465 default=None,
466 466 )
467 467 coreconfigitem('experimental', 'bundlecomplevel.gzip',
468 468 default=None,
469 469 )
470 470 coreconfigitem('experimental', 'bundlecomplevel.none',
471 471 default=None,
472 472 )
473 473 coreconfigitem('experimental', 'bundlecomplevel.zstd',
474 474 default=None,
475 475 )
476 476 coreconfigitem('experimental', 'changegroup3',
477 477 default=False,
478 478 )
479 479 coreconfigitem('experimental', 'clientcompressionengines',
480 480 default=list,
481 481 )
482 482 coreconfigitem('experimental', 'copytrace',
483 483 default='on',
484 484 )
485 485 coreconfigitem('experimental', 'copytrace.movecandidateslimit',
486 486 default=100,
487 487 )
488 488 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
489 489 default=100,
490 490 )
491 491 coreconfigitem('experimental', 'crecordtest',
492 492 default=None,
493 493 )
494 494 coreconfigitem('experimental', 'directaccess',
495 495 default=False,
496 496 )
497 497 coreconfigitem('experimental', 'directaccess.revnums',
498 498 default=False,
499 499 )
500 500 coreconfigitem('experimental', 'editortmpinhg',
501 501 default=False,
502 502 )
503 503 coreconfigitem('experimental', 'evolution',
504 504 default=list,
505 505 )
506 506 coreconfigitem('experimental', 'evolution.allowdivergence',
507 507 default=False,
508 508 alias=[('experimental', 'allowdivergence')]
509 509 )
510 510 coreconfigitem('experimental', 'evolution.allowunstable',
511 511 default=None,
512 512 )
513 513 coreconfigitem('experimental', 'evolution.createmarkers',
514 514 default=None,
515 515 )
516 516 coreconfigitem('experimental', 'evolution.effect-flags',
517 517 default=True,
518 518 alias=[('experimental', 'effect-flags')]
519 519 )
520 520 coreconfigitem('experimental', 'evolution.exchange',
521 521 default=None,
522 522 )
523 523 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
524 524 default=False,
525 525 )
526 526 coreconfigitem('experimental', 'evolution.report-instabilities',
527 527 default=True,
528 528 )
529 529 coreconfigitem('experimental', 'evolution.track-operation',
530 530 default=True,
531 531 )
532 532 coreconfigitem('experimental', 'maxdeltachainspan',
533 533 default=-1,
534 534 )
535 535 coreconfigitem('experimental', 'mergetempdirprefix',
536 536 default=None,
537 537 )
538 538 coreconfigitem('experimental', 'mmapindexthreshold',
539 539 default=None,
540 540 )
541 541 coreconfigitem('experimental', 'narrow',
542 542 default=False,
543 543 )
544 544 coreconfigitem('experimental', 'nonnormalparanoidcheck',
545 545 default=False,
546 546 )
547 547 coreconfigitem('experimental', 'exportableenviron',
548 548 default=list,
549 549 )
550 550 coreconfigitem('experimental', 'extendedheader.index',
551 551 default=None,
552 552 )
553 553 coreconfigitem('experimental', 'extendedheader.similarity',
554 554 default=False,
555 555 )
556 556 coreconfigitem('experimental', 'format.compression',
557 557 default='zlib',
558 558 )
559 559 coreconfigitem('experimental', 'graphshorten',
560 560 default=False,
561 561 )
562 562 coreconfigitem('experimental', 'graphstyle.parent',
563 563 default=dynamicdefault,
564 564 )
565 565 coreconfigitem('experimental', 'graphstyle.missing',
566 566 default=dynamicdefault,
567 567 )
568 568 coreconfigitem('experimental', 'graphstyle.grandparent',
569 569 default=dynamicdefault,
570 570 )
571 571 coreconfigitem('experimental', 'hook-track-tags',
572 572 default=False,
573 573 )
574 574 coreconfigitem('experimental', 'httppeer.advertise-v2',
575 575 default=False,
576 576 )
577 577 coreconfigitem('experimental', 'httppeer.v2-encoder-order',
578 578 default=None,
579 579 )
580 580 coreconfigitem('experimental', 'httppostargs',
581 581 default=False,
582 582 )
583 583 coreconfigitem('experimental', 'mergedriver',
584 584 default=None,
585 585 )
586 586 coreconfigitem('experimental', 'nointerrupt', default=False)
587 587 coreconfigitem('experimental', 'nointerrupt-interactiveonly', default=True)
588 588
589 589 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
590 590 default=False,
591 591 )
592 592 coreconfigitem('experimental', 'remotenames',
593 593 default=False,
594 594 )
595 595 coreconfigitem('experimental', 'removeemptydirs',
596 596 default=True,
597 597 )
598 598 coreconfigitem('experimental', 'revisions.prefixhexnode',
599 599 default=False,
600 600 )
601 601 coreconfigitem('experimental', 'revlogv2',
602 602 default=None,
603 603 )
604 604 coreconfigitem('experimental', 'revisions.disambiguatewithin',
605 605 default=None,
606 606 )
607 coreconfigitem('experimental', 'server.manifestdata.recommended-batch-size',
608 default=100000,
609 )
607 610 coreconfigitem('experimental', 'single-head-per-branch',
608 611 default=False,
609 612 )
610 613 coreconfigitem('experimental', 'sshserver.support-v2',
611 614 default=False,
612 615 )
613 616 coreconfigitem('experimental', 'spacemovesdown',
614 617 default=False,
615 618 )
616 619 coreconfigitem('experimental', 'sparse-read',
617 620 default=False,
618 621 )
619 622 coreconfigitem('experimental', 'sparse-read.density-threshold',
620 623 default=0.50,
621 624 )
622 625 coreconfigitem('experimental', 'sparse-read.min-gap-size',
623 626 default='65K',
624 627 )
625 628 coreconfigitem('experimental', 'treemanifest',
626 629 default=False,
627 630 )
628 631 coreconfigitem('experimental', 'update.atomic-file',
629 632 default=False,
630 633 )
631 634 coreconfigitem('experimental', 'sshpeer.advertise-v2',
632 635 default=False,
633 636 )
634 637 coreconfigitem('experimental', 'web.apiserver',
635 638 default=False,
636 639 )
637 640 coreconfigitem('experimental', 'web.api.http-v2',
638 641 default=False,
639 642 )
640 643 coreconfigitem('experimental', 'web.api.debugreflect',
641 644 default=False,
642 645 )
643 646 coreconfigitem('experimental', 'worker.wdir-get-thread-safe',
644 647 default=False,
645 648 )
646 649 coreconfigitem('experimental', 'xdiff',
647 650 default=False,
648 651 )
649 652 coreconfigitem('extensions', '.*',
650 653 default=None,
651 654 generic=True,
652 655 )
653 656 coreconfigitem('extdata', '.*',
654 657 default=None,
655 658 generic=True,
656 659 )
657 660 coreconfigitem('format', 'chunkcachesize',
658 661 default=None,
659 662 )
660 663 coreconfigitem('format', 'dotencode',
661 664 default=True,
662 665 )
663 666 coreconfigitem('format', 'generaldelta',
664 667 default=False,
665 668 )
666 669 coreconfigitem('format', 'manifestcachesize',
667 670 default=None,
668 671 )
669 672 coreconfigitem('format', 'maxchainlen',
670 673 default=dynamicdefault,
671 674 )
672 675 coreconfigitem('format', 'obsstore-version',
673 676 default=None,
674 677 )
675 678 coreconfigitem('format', 'sparse-revlog',
676 679 default=False,
677 680 )
678 681 coreconfigitem('format', 'usefncache',
679 682 default=True,
680 683 )
681 684 coreconfigitem('format', 'usegeneraldelta',
682 685 default=True,
683 686 )
684 687 coreconfigitem('format', 'usestore',
685 688 default=True,
686 689 )
687 690 coreconfigitem('format', 'internal-phase',
688 691 default=False,
689 692 )
690 693 coreconfigitem('fsmonitor', 'warn_when_unused',
691 694 default=True,
692 695 )
693 696 coreconfigitem('fsmonitor', 'warn_update_file_count',
694 697 default=50000,
695 698 )
696 699 coreconfigitem('hooks', '.*',
697 700 default=dynamicdefault,
698 701 generic=True,
699 702 )
700 703 coreconfigitem('hgweb-paths', '.*',
701 704 default=list,
702 705 generic=True,
703 706 )
704 707 coreconfigitem('hostfingerprints', '.*',
705 708 default=list,
706 709 generic=True,
707 710 )
708 711 coreconfigitem('hostsecurity', 'ciphers',
709 712 default=None,
710 713 )
711 714 coreconfigitem('hostsecurity', 'disabletls10warning',
712 715 default=False,
713 716 )
714 717 coreconfigitem('hostsecurity', 'minimumprotocol',
715 718 default=dynamicdefault,
716 719 )
717 720 coreconfigitem('hostsecurity', '.*:minimumprotocol$',
718 721 default=dynamicdefault,
719 722 generic=True,
720 723 )
721 724 coreconfigitem('hostsecurity', '.*:ciphers$',
722 725 default=dynamicdefault,
723 726 generic=True,
724 727 )
725 728 coreconfigitem('hostsecurity', '.*:fingerprints$',
726 729 default=list,
727 730 generic=True,
728 731 )
729 732 coreconfigitem('hostsecurity', '.*:verifycertsfile$',
730 733 default=None,
731 734 generic=True,
732 735 )
733 736
734 737 coreconfigitem('http_proxy', 'always',
735 738 default=False,
736 739 )
737 740 coreconfigitem('http_proxy', 'host',
738 741 default=None,
739 742 )
740 743 coreconfigitem('http_proxy', 'no',
741 744 default=list,
742 745 )
743 746 coreconfigitem('http_proxy', 'passwd',
744 747 default=None,
745 748 )
746 749 coreconfigitem('http_proxy', 'user',
747 750 default=None,
748 751 )
749 752
750 753 coreconfigitem('http', 'timeout',
751 754 default=None,
752 755 )
753 756
754 757 coreconfigitem('logtoprocess', 'commandexception',
755 758 default=None,
756 759 )
757 760 coreconfigitem('logtoprocess', 'commandfinish',
758 761 default=None,
759 762 )
760 763 coreconfigitem('logtoprocess', 'command',
761 764 default=None,
762 765 )
763 766 coreconfigitem('logtoprocess', 'develwarn',
764 767 default=None,
765 768 )
766 769 coreconfigitem('logtoprocess', 'uiblocked',
767 770 default=None,
768 771 )
769 772 coreconfigitem('merge', 'checkunknown',
770 773 default='abort',
771 774 )
772 775 coreconfigitem('merge', 'checkignored',
773 776 default='abort',
774 777 )
775 778 coreconfigitem('experimental', 'merge.checkpathconflicts',
776 779 default=False,
777 780 )
778 781 coreconfigitem('merge', 'followcopies',
779 782 default=True,
780 783 )
781 784 coreconfigitem('merge', 'on-failure',
782 785 default='continue',
783 786 )
784 787 coreconfigitem('merge', 'preferancestor',
785 788 default=lambda: ['*'],
786 789 )
787 790 coreconfigitem('merge', 'strict-capability-check',
788 791 default=False,
789 792 )
790 793 coreconfigitem('merge-tools', '.*',
791 794 default=None,
792 795 generic=True,
793 796 )
794 797 coreconfigitem('merge-tools', br'.*\.args$',
795 798 default="$local $base $other",
796 799 generic=True,
797 800 priority=-1,
798 801 )
799 802 coreconfigitem('merge-tools', br'.*\.binary$',
800 803 default=False,
801 804 generic=True,
802 805 priority=-1,
803 806 )
804 807 coreconfigitem('merge-tools', br'.*\.check$',
805 808 default=list,
806 809 generic=True,
807 810 priority=-1,
808 811 )
809 812 coreconfigitem('merge-tools', br'.*\.checkchanged$',
810 813 default=False,
811 814 generic=True,
812 815 priority=-1,
813 816 )
814 817 coreconfigitem('merge-tools', br'.*\.executable$',
815 818 default=dynamicdefault,
816 819 generic=True,
817 820 priority=-1,
818 821 )
819 822 coreconfigitem('merge-tools', br'.*\.fixeol$',
820 823 default=False,
821 824 generic=True,
822 825 priority=-1,
823 826 )
824 827 coreconfigitem('merge-tools', br'.*\.gui$',
825 828 default=False,
826 829 generic=True,
827 830 priority=-1,
828 831 )
829 832 coreconfigitem('merge-tools', br'.*\.mergemarkers$',
830 833 default='basic',
831 834 generic=True,
832 835 priority=-1,
833 836 )
834 837 coreconfigitem('merge-tools', br'.*\.mergemarkertemplate$',
835 838 default=dynamicdefault, # take from ui.mergemarkertemplate
836 839 generic=True,
837 840 priority=-1,
838 841 )
839 842 coreconfigitem('merge-tools', br'.*\.priority$',
840 843 default=0,
841 844 generic=True,
842 845 priority=-1,
843 846 )
844 847 coreconfigitem('merge-tools', br'.*\.premerge$',
845 848 default=dynamicdefault,
846 849 generic=True,
847 850 priority=-1,
848 851 )
849 852 coreconfigitem('merge-tools', br'.*\.symlink$',
850 853 default=False,
851 854 generic=True,
852 855 priority=-1,
853 856 )
854 857 coreconfigitem('pager', 'attend-.*',
855 858 default=dynamicdefault,
856 859 generic=True,
857 860 )
858 861 coreconfigitem('pager', 'ignore',
859 862 default=list,
860 863 )
861 864 coreconfigitem('pager', 'pager',
862 865 default=dynamicdefault,
863 866 )
864 867 coreconfigitem('patch', 'eol',
865 868 default='strict',
866 869 )
867 870 coreconfigitem('patch', 'fuzz',
868 871 default=2,
869 872 )
870 873 coreconfigitem('paths', 'default',
871 874 default=None,
872 875 )
873 876 coreconfigitem('paths', 'default-push',
874 877 default=None,
875 878 )
876 879 coreconfigitem('paths', '.*',
877 880 default=None,
878 881 generic=True,
879 882 )
880 883 coreconfigitem('phases', 'checksubrepos',
881 884 default='follow',
882 885 )
883 886 coreconfigitem('phases', 'new-commit',
884 887 default='draft',
885 888 )
886 889 coreconfigitem('phases', 'publish',
887 890 default=True,
888 891 )
889 892 coreconfigitem('profiling', 'enabled',
890 893 default=False,
891 894 )
892 895 coreconfigitem('profiling', 'format',
893 896 default='text',
894 897 )
895 898 coreconfigitem('profiling', 'freq',
896 899 default=1000,
897 900 )
898 901 coreconfigitem('profiling', 'limit',
899 902 default=30,
900 903 )
901 904 coreconfigitem('profiling', 'nested',
902 905 default=0,
903 906 )
904 907 coreconfigitem('profiling', 'output',
905 908 default=None,
906 909 )
907 910 coreconfigitem('profiling', 'showmax',
908 911 default=0.999,
909 912 )
910 913 coreconfigitem('profiling', 'showmin',
911 914 default=dynamicdefault,
912 915 )
913 916 coreconfigitem('profiling', 'sort',
914 917 default='inlinetime',
915 918 )
916 919 coreconfigitem('profiling', 'statformat',
917 920 default='hotpath',
918 921 )
919 922 coreconfigitem('profiling', 'time-track',
920 923 default='cpu',
921 924 )
922 925 coreconfigitem('profiling', 'type',
923 926 default='stat',
924 927 )
925 928 coreconfigitem('progress', 'assume-tty',
926 929 default=False,
927 930 )
928 931 coreconfigitem('progress', 'changedelay',
929 932 default=1,
930 933 )
931 934 coreconfigitem('progress', 'clear-complete',
932 935 default=True,
933 936 )
934 937 coreconfigitem('progress', 'debug',
935 938 default=False,
936 939 )
937 940 coreconfigitem('progress', 'delay',
938 941 default=3,
939 942 )
940 943 coreconfigitem('progress', 'disable',
941 944 default=False,
942 945 )
943 946 coreconfigitem('progress', 'estimateinterval',
944 947 default=60.0,
945 948 )
946 949 coreconfigitem('progress', 'format',
947 950 default=lambda: ['topic', 'bar', 'number', 'estimate'],
948 951 )
949 952 coreconfigitem('progress', 'refresh',
950 953 default=0.1,
951 954 )
952 955 coreconfigitem('progress', 'width',
953 956 default=dynamicdefault,
954 957 )
955 958 coreconfigitem('push', 'pushvars.server',
956 959 default=False,
957 960 )
958 961 coreconfigitem('storage', 'new-repo-backend',
959 962 default='revlogv1',
960 963 )
961 964 coreconfigitem('storage', 'revlog.optimize-delta-parent-choice',
962 965 default=True,
963 966 alias=[('format', 'aggressivemergedeltas')],
964 967 )
965 968 coreconfigitem('server', 'bookmarks-pushkey-compat',
966 969 default=True,
967 970 )
968 971 coreconfigitem('server', 'bundle1',
969 972 default=True,
970 973 )
971 974 coreconfigitem('server', 'bundle1gd',
972 975 default=None,
973 976 )
974 977 coreconfigitem('server', 'bundle1.pull',
975 978 default=None,
976 979 )
977 980 coreconfigitem('server', 'bundle1gd.pull',
978 981 default=None,
979 982 )
980 983 coreconfigitem('server', 'bundle1.push',
981 984 default=None,
982 985 )
983 986 coreconfigitem('server', 'bundle1gd.push',
984 987 default=None,
985 988 )
986 989 coreconfigitem('server', 'bundle2.stream',
987 990 default=True,
988 991 alias=[('experimental', 'bundle2.stream')]
989 992 )
990 993 coreconfigitem('server', 'compressionengines',
991 994 default=list,
992 995 )
993 996 coreconfigitem('server', 'concurrent-push-mode',
994 997 default='strict',
995 998 )
996 999 coreconfigitem('server', 'disablefullbundle',
997 1000 default=False,
998 1001 )
999 1002 coreconfigitem('server', 'maxhttpheaderlen',
1000 1003 default=1024,
1001 1004 )
1002 1005 coreconfigitem('server', 'pullbundle',
1003 1006 default=False,
1004 1007 )
1005 1008 coreconfigitem('server', 'preferuncompressed',
1006 1009 default=False,
1007 1010 )
1008 1011 coreconfigitem('server', 'streamunbundle',
1009 1012 default=False,
1010 1013 )
1011 1014 coreconfigitem('server', 'uncompressed',
1012 1015 default=True,
1013 1016 )
1014 1017 coreconfigitem('server', 'uncompressedallowsecret',
1015 1018 default=False,
1016 1019 )
1017 1020 coreconfigitem('server', 'validate',
1018 1021 default=False,
1019 1022 )
1020 1023 coreconfigitem('server', 'zliblevel',
1021 1024 default=-1,
1022 1025 )
1023 1026 coreconfigitem('server', 'zstdlevel',
1024 1027 default=3,
1025 1028 )
1026 1029 coreconfigitem('share', 'pool',
1027 1030 default=None,
1028 1031 )
1029 1032 coreconfigitem('share', 'poolnaming',
1030 1033 default='identity',
1031 1034 )
1032 1035 coreconfigitem('smtp', 'host',
1033 1036 default=None,
1034 1037 )
1035 1038 coreconfigitem('smtp', 'local_hostname',
1036 1039 default=None,
1037 1040 )
1038 1041 coreconfigitem('smtp', 'password',
1039 1042 default=None,
1040 1043 )
1041 1044 coreconfigitem('smtp', 'port',
1042 1045 default=dynamicdefault,
1043 1046 )
1044 1047 coreconfigitem('smtp', 'tls',
1045 1048 default='none',
1046 1049 )
1047 1050 coreconfigitem('smtp', 'username',
1048 1051 default=None,
1049 1052 )
1050 1053 coreconfigitem('sparse', 'missingwarning',
1051 1054 default=True,
1052 1055 )
1053 1056 coreconfigitem('subrepos', 'allowed',
1054 1057 default=dynamicdefault, # to make backporting simpler
1055 1058 )
1056 1059 coreconfigitem('subrepos', 'hg:allowed',
1057 1060 default=dynamicdefault,
1058 1061 )
1059 1062 coreconfigitem('subrepos', 'git:allowed',
1060 1063 default=dynamicdefault,
1061 1064 )
1062 1065 coreconfigitem('subrepos', 'svn:allowed',
1063 1066 default=dynamicdefault,
1064 1067 )
1065 1068 coreconfigitem('templates', '.*',
1066 1069 default=None,
1067 1070 generic=True,
1068 1071 )
1069 1072 coreconfigitem('trusted', 'groups',
1070 1073 default=list,
1071 1074 )
1072 1075 coreconfigitem('trusted', 'users',
1073 1076 default=list,
1074 1077 )
1075 1078 coreconfigitem('ui', '_usedassubrepo',
1076 1079 default=False,
1077 1080 )
1078 1081 coreconfigitem('ui', 'allowemptycommit',
1079 1082 default=False,
1080 1083 )
1081 1084 coreconfigitem('ui', 'archivemeta',
1082 1085 default=True,
1083 1086 )
1084 1087 coreconfigitem('ui', 'askusername',
1085 1088 default=False,
1086 1089 )
1087 1090 coreconfigitem('ui', 'clonebundlefallback',
1088 1091 default=False,
1089 1092 )
1090 1093 coreconfigitem('ui', 'clonebundleprefers',
1091 1094 default=list,
1092 1095 )
1093 1096 coreconfigitem('ui', 'clonebundles',
1094 1097 default=True,
1095 1098 )
1096 1099 coreconfigitem('ui', 'color',
1097 1100 default='auto',
1098 1101 )
1099 1102 coreconfigitem('ui', 'commitsubrepos',
1100 1103 default=False,
1101 1104 )
1102 1105 coreconfigitem('ui', 'debug',
1103 1106 default=False,
1104 1107 )
1105 1108 coreconfigitem('ui', 'debugger',
1106 1109 default=None,
1107 1110 )
1108 1111 coreconfigitem('ui', 'editor',
1109 1112 default=dynamicdefault,
1110 1113 )
1111 1114 coreconfigitem('ui', 'fallbackencoding',
1112 1115 default=None,
1113 1116 )
1114 1117 coreconfigitem('ui', 'forcecwd',
1115 1118 default=None,
1116 1119 )
1117 1120 coreconfigitem('ui', 'forcemerge',
1118 1121 default=None,
1119 1122 )
1120 1123 coreconfigitem('ui', 'formatdebug',
1121 1124 default=False,
1122 1125 )
1123 1126 coreconfigitem('ui', 'formatjson',
1124 1127 default=False,
1125 1128 )
1126 1129 coreconfigitem('ui', 'formatted',
1127 1130 default=None,
1128 1131 )
1129 1132 coreconfigitem('ui', 'graphnodetemplate',
1130 1133 default=None,
1131 1134 )
1132 1135 coreconfigitem('ui', 'history-editing-backup',
1133 1136 default=True,
1134 1137 )
1135 1138 coreconfigitem('ui', 'interactive',
1136 1139 default=None,
1137 1140 )
1138 1141 coreconfigitem('ui', 'interface',
1139 1142 default=None,
1140 1143 )
1141 1144 coreconfigitem('ui', 'interface.chunkselector',
1142 1145 default=None,
1143 1146 )
1144 1147 coreconfigitem('ui', 'large-file-limit',
1145 1148 default=10000000,
1146 1149 )
1147 1150 coreconfigitem('ui', 'logblockedtimes',
1148 1151 default=False,
1149 1152 )
1150 1153 coreconfigitem('ui', 'logtemplate',
1151 1154 default=None,
1152 1155 )
1153 1156 coreconfigitem('ui', 'merge',
1154 1157 default=None,
1155 1158 )
1156 1159 coreconfigitem('ui', 'mergemarkers',
1157 1160 default='basic',
1158 1161 )
1159 1162 coreconfigitem('ui', 'mergemarkertemplate',
1160 1163 default=('{node|short} '
1161 1164 '{ifeq(tags, "tip", "", '
1162 1165 'ifeq(tags, "", "", "{tags} "))}'
1163 1166 '{if(bookmarks, "{bookmarks} ")}'
1164 1167 '{ifeq(branch, "default", "", "{branch} ")}'
1165 1168 '- {author|user}: {desc|firstline}')
1166 1169 )
1167 1170 coreconfigitem('ui', 'nontty',
1168 1171 default=False,
1169 1172 )
1170 1173 coreconfigitem('ui', 'origbackuppath',
1171 1174 default=None,
1172 1175 )
1173 1176 coreconfigitem('ui', 'paginate',
1174 1177 default=True,
1175 1178 )
1176 1179 coreconfigitem('ui', 'patch',
1177 1180 default=None,
1178 1181 )
1179 1182 coreconfigitem('ui', 'portablefilenames',
1180 1183 default='warn',
1181 1184 )
1182 1185 coreconfigitem('ui', 'promptecho',
1183 1186 default=False,
1184 1187 )
1185 1188 coreconfigitem('ui', 'quiet',
1186 1189 default=False,
1187 1190 )
1188 1191 coreconfigitem('ui', 'quietbookmarkmove',
1189 1192 default=False,
1190 1193 )
1191 1194 coreconfigitem('ui', 'remotecmd',
1192 1195 default='hg',
1193 1196 )
1194 1197 coreconfigitem('ui', 'report_untrusted',
1195 1198 default=True,
1196 1199 )
1197 1200 coreconfigitem('ui', 'rollback',
1198 1201 default=True,
1199 1202 )
1200 1203 coreconfigitem('ui', 'signal-safe-lock',
1201 1204 default=True,
1202 1205 )
1203 1206 coreconfigitem('ui', 'slash',
1204 1207 default=False,
1205 1208 )
1206 1209 coreconfigitem('ui', 'ssh',
1207 1210 default='ssh',
1208 1211 )
1209 1212 coreconfigitem('ui', 'ssherrorhint',
1210 1213 default=None,
1211 1214 )
1212 1215 coreconfigitem('ui', 'statuscopies',
1213 1216 default=False,
1214 1217 )
1215 1218 coreconfigitem('ui', 'strict',
1216 1219 default=False,
1217 1220 )
1218 1221 coreconfigitem('ui', 'style',
1219 1222 default='',
1220 1223 )
1221 1224 coreconfigitem('ui', 'supportcontact',
1222 1225 default=None,
1223 1226 )
1224 1227 coreconfigitem('ui', 'textwidth',
1225 1228 default=78,
1226 1229 )
1227 1230 coreconfigitem('ui', 'timeout',
1228 1231 default='600',
1229 1232 )
1230 1233 coreconfigitem('ui', 'timeout.warn',
1231 1234 default=0,
1232 1235 )
1233 1236 coreconfigitem('ui', 'traceback',
1234 1237 default=False,
1235 1238 )
1236 1239 coreconfigitem('ui', 'tweakdefaults',
1237 1240 default=False,
1238 1241 )
1239 1242 coreconfigitem('ui', 'username',
1240 1243 alias=[('ui', 'user')]
1241 1244 )
1242 1245 coreconfigitem('ui', 'verbose',
1243 1246 default=False,
1244 1247 )
1245 1248 coreconfigitem('verify', 'skipflags',
1246 1249 default=None,
1247 1250 )
1248 1251 coreconfigitem('web', 'allowbz2',
1249 1252 default=False,
1250 1253 )
1251 1254 coreconfigitem('web', 'allowgz',
1252 1255 default=False,
1253 1256 )
1254 1257 coreconfigitem('web', 'allow-pull',
1255 1258 alias=[('web', 'allowpull')],
1256 1259 default=True,
1257 1260 )
1258 1261 coreconfigitem('web', 'allow-push',
1259 1262 alias=[('web', 'allow_push')],
1260 1263 default=list,
1261 1264 )
1262 1265 coreconfigitem('web', 'allowzip',
1263 1266 default=False,
1264 1267 )
1265 1268 coreconfigitem('web', 'archivesubrepos',
1266 1269 default=False,
1267 1270 )
1268 1271 coreconfigitem('web', 'cache',
1269 1272 default=True,
1270 1273 )
1271 1274 coreconfigitem('web', 'contact',
1272 1275 default=None,
1273 1276 )
1274 1277 coreconfigitem('web', 'deny_push',
1275 1278 default=list,
1276 1279 )
1277 1280 coreconfigitem('web', 'guessmime',
1278 1281 default=False,
1279 1282 )
1280 1283 coreconfigitem('web', 'hidden',
1281 1284 default=False,
1282 1285 )
1283 1286 coreconfigitem('web', 'labels',
1284 1287 default=list,
1285 1288 )
1286 1289 coreconfigitem('web', 'logoimg',
1287 1290 default='hglogo.png',
1288 1291 )
1289 1292 coreconfigitem('web', 'logourl',
1290 1293 default='https://mercurial-scm.org/',
1291 1294 )
1292 1295 coreconfigitem('web', 'accesslog',
1293 1296 default='-',
1294 1297 )
1295 1298 coreconfigitem('web', 'address',
1296 1299 default='',
1297 1300 )
1298 1301 coreconfigitem('web', 'allow-archive',
1299 1302 alias=[('web', 'allow_archive')],
1300 1303 default=list,
1301 1304 )
1302 1305 coreconfigitem('web', 'allow_read',
1303 1306 default=list,
1304 1307 )
1305 1308 coreconfigitem('web', 'baseurl',
1306 1309 default=None,
1307 1310 )
1308 1311 coreconfigitem('web', 'cacerts',
1309 1312 default=None,
1310 1313 )
1311 1314 coreconfigitem('web', 'certificate',
1312 1315 default=None,
1313 1316 )
1314 1317 coreconfigitem('web', 'collapse',
1315 1318 default=False,
1316 1319 )
1317 1320 coreconfigitem('web', 'csp',
1318 1321 default=None,
1319 1322 )
1320 1323 coreconfigitem('web', 'deny_read',
1321 1324 default=list,
1322 1325 )
1323 1326 coreconfigitem('web', 'descend',
1324 1327 default=True,
1325 1328 )
1326 1329 coreconfigitem('web', 'description',
1327 1330 default="",
1328 1331 )
1329 1332 coreconfigitem('web', 'encoding',
1330 1333 default=lambda: encoding.encoding,
1331 1334 )
1332 1335 coreconfigitem('web', 'errorlog',
1333 1336 default='-',
1334 1337 )
1335 1338 coreconfigitem('web', 'ipv6',
1336 1339 default=False,
1337 1340 )
1338 1341 coreconfigitem('web', 'maxchanges',
1339 1342 default=10,
1340 1343 )
1341 1344 coreconfigitem('web', 'maxfiles',
1342 1345 default=10,
1343 1346 )
1344 1347 coreconfigitem('web', 'maxshortchanges',
1345 1348 default=60,
1346 1349 )
1347 1350 coreconfigitem('web', 'motd',
1348 1351 default='',
1349 1352 )
1350 1353 coreconfigitem('web', 'name',
1351 1354 default=dynamicdefault,
1352 1355 )
1353 1356 coreconfigitem('web', 'port',
1354 1357 default=8000,
1355 1358 )
1356 1359 coreconfigitem('web', 'prefix',
1357 1360 default='',
1358 1361 )
1359 1362 coreconfigitem('web', 'push_ssl',
1360 1363 default=True,
1361 1364 )
1362 1365 coreconfigitem('web', 'refreshinterval',
1363 1366 default=20,
1364 1367 )
1365 1368 coreconfigitem('web', 'server-header',
1366 1369 default=None,
1367 1370 )
1368 1371 coreconfigitem('web', 'static',
1369 1372 default=None,
1370 1373 )
1371 1374 coreconfigitem('web', 'staticurl',
1372 1375 default=None,
1373 1376 )
1374 1377 coreconfigitem('web', 'stripes',
1375 1378 default=1,
1376 1379 )
1377 1380 coreconfigitem('web', 'style',
1378 1381 default='paper',
1379 1382 )
1380 1383 coreconfigitem('web', 'templates',
1381 1384 default=None,
1382 1385 )
1383 1386 coreconfigitem('web', 'view',
1384 1387 default='served',
1385 1388 )
1386 1389 coreconfigitem('worker', 'backgroundclose',
1387 1390 default=dynamicdefault,
1388 1391 )
1389 1392 # Windows defaults to a limit of 512 open files. A buffer of 128
1390 1393 # should give us enough headway.
1391 1394 coreconfigitem('worker', 'backgroundclosemaxqueue',
1392 1395 default=384,
1393 1396 )
1394 1397 coreconfigitem('worker', 'backgroundcloseminfilecount',
1395 1398 default=2048,
1396 1399 )
1397 1400 coreconfigitem('worker', 'backgroundclosethreadcount',
1398 1401 default=4,
1399 1402 )
1400 1403 coreconfigitem('worker', 'enabled',
1401 1404 default=True,
1402 1405 )
1403 1406 coreconfigitem('worker', 'numcpus',
1404 1407 default=None,
1405 1408 )
1406 1409
1407 1410 # Rebase related configuration moved to core because other extension are doing
1408 1411 # strange things. For example, shelve import the extensions to reuse some bit
1409 1412 # without formally loading it.
1410 1413 coreconfigitem('commands', 'rebase.requiredest',
1411 1414 default=False,
1412 1415 )
1413 1416 coreconfigitem('experimental', 'rebaseskipobsolete',
1414 1417 default=True,
1415 1418 )
1416 1419 coreconfigitem('rebase', 'singletransaction',
1417 1420 default=False,
1418 1421 )
1419 1422 coreconfigitem('rebase', 'experimental.inmemory',
1420 1423 default=False,
1421 1424 )
@@ -1,529 +1,542 b''
1 1 **Experimental and under active development**
2 2
3 3 This section documents the wire protocol commands exposed to transports
4 4 using the frame-based protocol. The set of commands exposed through
5 5 these transports is distinct from the set of commands exposed to legacy
6 6 transports.
7 7
8 8 The frame-based protocol uses CBOR to encode command execution requests.
9 9 All command arguments must be mapped to a specific or set of CBOR data
10 10 types.
11 11
12 12 The response to many commands is also CBOR. There is no common response
13 13 format: each command defines its own response format.
14 14
15 15 TODOs
16 16 =====
17 17
18 18 * Add "node namespace" support to each command. In order to support
19 19 SHA-1 hash transition, we want servers to be able to expose different
20 20 "node namespaces" for the same data. Every command operating on nodes
21 21 should specify which "node namespace" it is operating on and responses
22 22 should encode the "node namespace" accordingly.
23 23
24 24 Commands
25 25 ========
26 26
27 27 The sections below detail all commands available to wire protocol version
28 28 2.
29 29
30 30 branchmap
31 31 ---------
32 32
33 33 Obtain heads in named branches.
34 34
35 35 Receives no arguments.
36 36
37 37 The response is a map with bytestring keys defining the branch name.
38 38 Values are arrays of bytestring defining raw changeset nodes.
39 39
40 40 capabilities
41 41 ------------
42 42
43 43 Obtain the server's capabilities.
44 44
45 45 Receives no arguments.
46 46
47 47 This command is typically called only as part of the handshake during
48 48 initial connection establishment.
49 49
50 50 The response is a map with bytestring keys defining server information.
51 51
52 52 The defined keys are:
53 53
54 54 commands
55 55 A map defining available wire protocol commands on this server.
56 56
57 57 Keys in the map are the names of commands that can be invoked. Values
58 58 are maps defining information about that command. The bytestring keys
59 59 are:
60 60
61 61 args
62 62 (map) Describes arguments accepted by the command.
63 63
64 64 Keys are bytestrings denoting the argument name.
65 65
66 66 Values are maps describing the argument. The map has the following
67 67 bytestring keys:
68 68
69 69 default
70 70 (varied) The default value for this argument if not specified. Only
71 71 present if ``required`` is not true.
72 72
73 73 required
74 74 (boolean) Whether the argument must be specified. Failure to send
75 75 required arguments will result in an error executing the command.
76 76
77 77 type
78 78 (bytestring) The type of the argument. e.g. ``bytes`` or ``bool``.
79 79
80 80 validvalues
81 81 (set) Values that are recognized for this argument. Some arguments
82 82 only allow a fixed set of values to be specified. These arguments
83 83 may advertise that set in this key. If this set is advertised and
84 84 a value not in this set is specified, the command should result
85 85 in error.
86 86
87 87 permissions
88 88 An array of permissions required to execute this command.
89 89
90 *
91 (various) Individual commands may define extra keys that supplement
92 generic command metadata. See the command definition for more.
93
90 94 framingmediatypes
91 95 An array of bytestrings defining the supported framing protocol
92 96 media types. Servers will not accept media types not in this list.
93 97
94 98 pathfilterprefixes
95 99 (set of bytestring) Matcher prefixes that are recognized when performing
96 100 path filtering. Specifying a path filter whose type/prefix does not
97 101 match one in this set will likely be rejected by the server.
98 102
99 103 rawrepoformats
100 104 An array of storage formats the repository is using. This set of
101 105 requirements can be used to determine whether a client can read a
102 106 *raw* copy of file data available.
103 107
104 108 redirect
105 109 A map declaring potential *content redirects* that may be used by this
106 110 server. Contains the following bytestring keys:
107 111
108 112 targets
109 113 (array of maps) Potential redirect targets. Values are maps describing
110 114 this target in more detail. Each map has the following bytestring keys:
111 115
112 116 name
113 117 (bytestring) Identifier for this target. The identifier will be used
114 118 by clients to uniquely identify this target.
115 119
116 120 protocol
117 121 (bytestring) High-level network protocol. Values can be
118 122 ``http``, ```https``, ``ssh``, etc.
119 123
120 124 uris
121 125 (array of bytestrings) Representative URIs for this target.
122 126
123 127 snirequired (optional)
124 128 (boolean) Indicates whether Server Name Indication is required
125 129 to use this target. Defaults to False.
126 130
127 131 tlsversions (optional)
128 132 (array of bytestring) Indicates which TLS versions are supported by
129 133 this target. Values are ``1.1``, ``1.2``, ``1.3``, etc.
130 134
131 135 hashes
132 136 (array of bytestring) Indicates support for hashing algorithms that are
133 137 used to ensure content integrity. Values include ``sha1``, ``sha256``,
134 138 etc.
135 139
136 140 changesetdata
137 141 -------------
138 142
139 143 Obtain various data related to changesets.
140 144
141 145 The command accepts the following arguments:
142 146
143 147 noderange
144 148 (array of arrays of bytestrings) An array of 2 elements, each being an
145 149 array of node bytestrings. The first array denotes the changelog revisions
146 150 that are already known to the client. The second array denotes the changelog
147 151 revision DAG heads to fetch. The argument essentially defines a DAG range
148 152 bounded by root and head nodes to fetch.
149 153
150 154 The roots array may be empty. The heads array must be defined.
151 155
152 156 nodes
153 157 (array of bytestrings) Changelog revisions to request explicitly.
154 158
155 159 nodesdepth
156 160 (unsigned integer) Number of ancestor revisions of elements in ``nodes``
157 161 to also fetch. When defined, for each element in ``nodes``, DAG ancestors
158 162 will be walked until at most N total revisions are emitted.
159 163
160 164 fields
161 165 (set of bytestring) Which data associated with changelog revisions to
162 166 fetch. The following values are recognized:
163 167
164 168 bookmarks
165 169 Bookmarks associated with a revision.
166 170
167 171 parents
168 172 Parent revisions.
169 173
170 174 phase
171 175 The phase state of a revision.
172 176
173 177 revision
174 178 The raw, revision data for the changelog entry. The hash of this data
175 179 will match the revision's node value.
176 180
177 181 The server resolves the set of revisions relevant to the request by taking
178 182 the union of the ``noderange`` and ``nodes`` arguments. At least one of these
179 183 arguments must be defined.
180 184
181 185 The response bytestream starts with a CBOR map describing the data that follows.
182 186 This map has the following bytestring keys:
183 187
184 188 totalitems
185 189 (unsigned integer) Total number of changelog revisions whose data is being
186 190 transferred. This maps to the set of revisions in the requested node
187 191 range, not the total number of records that follow (see below for why).
188 192
189 193 Following the map header is a series of 0 or more CBOR values. If values
190 194 are present, the first value will always be a map describing a single changeset
191 195 revision.
192 196
193 197 If the ``fieldsfollowing`` key is present, the map will immediately be followed
194 198 by N CBOR bytestring values, where N is the number of elements in
195 199 ``fieldsfollowing``. Each bytestring value corresponds to a field denoted
196 200 by ``fieldsfollowing``.
197 201
198 202 Following the optional bytestring field values is the next revision descriptor
199 203 map, or end of stream.
200 204
201 205 Each revision descriptor map has the following bytestring keys:
202 206
203 207 node
204 208 (bytestring) The node value for this revision. This is the SHA-1 hash of
205 209 the raw revision data.
206 210
207 211 bookmarks (optional)
208 212 (array of bytestrings) Bookmarks attached to this revision. Only present
209 213 if ``bookmarks`` data is being requested and the revision has bookmarks
210 214 attached.
211 215
212 216 fieldsfollowing (optional)
213 217 (array of 2-array) Denotes what fields immediately follow this map. Each
214 218 value is an array with 2 elements: the bytestring field name and an unsigned
215 219 integer describing the length of the data, in bytes.
216 220
217 221 If this key isn't present, no special fields will follow this map.
218 222
219 223 The following fields may be present:
220 224
221 225 revision
222 226 Raw, revision data for the changelog entry. Contains a serialized form
223 227 of the changeset data, including the author, date, commit message, set
224 228 of changed files, manifest node, and other metadata.
225 229
226 230 Only present if the ``revision`` field was requested.
227 231
228 232 parents (optional)
229 233 (array of bytestrings) The nodes representing the parent revisions of this
230 234 revision. Only present if ``parents`` data is being requested.
231 235
232 236 phase (optional)
233 237 (bytestring) The phase that a revision is in. Recognized values are
234 238 ``secret``, ``draft``, and ``public``. Only present if ``phase`` data
235 239 is being requested.
236 240
237 241 If nodes are requested via ``noderange``, they will be emitted in DAG order,
238 242 parents always before children.
239 243
240 244 If nodes are requested via ``nodes``, they will be emitted in requested order.
241 245
242 246 Nodes from ``nodes`` are emitted before nodes from ``noderange``.
243 247
244 248 The set of changeset revisions emitted may not match the exact set of
245 249 changesets requested. Furthermore, the set of keys present on each
246 250 map may vary. This is to facilitate emitting changeset updates as well
247 251 as new revisions.
248 252
249 253 For example, if the request wants ``phase`` and ``revision`` data,
250 254 the response may contain entries for each changeset in the common nodes
251 255 set with the ``phase`` key and without the ``revision`` key in order
252 256 to reflect a phase-only update.
253 257
254 258 TODO support different revision selection mechanisms (e.g. non-public, specific
255 259 revisions)
256 260 TODO support different hash "namespaces" for revisions (e.g. sha-1 versus other)
257 261 TODO support emitting obsolescence data
258 262 TODO support filtering based on relevant paths (narrow clone)
259 263 TODO support hgtagsfnodes cache / tags data
260 264 TODO support branch heads cache
261 265 TODO consider unify query mechanism. e.g. as an array of "query descriptors"
262 266 rather than a set of top-level arguments that have semantics when combined.
263 267
264 268 filedata
265 269 --------
266 270
267 271 Obtain various data related to an individual tracked file.
268 272
269 273 The command accepts the following arguments:
270 274
271 275 fields
272 276 (set of bytestring) Which data associated with a file to fetch.
273 277 The following values are recognized:
274 278
275 279 parents
276 280 Parent nodes for the revision.
277 281
278 282 revision
279 283 The raw revision data for a file.
280 284
281 285 haveparents
282 286 (bool) Whether the client has the parent revisions of all requested
283 287 nodes. If set, the server may emit revision data as deltas against
284 288 any parent revision. If not set, the server MUST only emit deltas for
285 289 revisions previously emitted by this command.
286 290
287 291 False is assumed in the absence of any value.
288 292
289 293 nodes
290 294 (array of bytestrings) File nodes whose data to retrieve.
291 295
292 296 path
293 297 (bytestring) Path of the tracked file whose data to retrieve.
294 298
295 299 TODO allow specifying revisions via alternate means (such as from
296 300 changeset revisions or ranges)
297 301
298 302 The response bytestream starts with a CBOR map describing the data that
299 303 follows. It has the following bytestream keys:
300 304
301 305 totalitems
302 306 (unsigned integer) Total number of file revisions whose data is
303 307 being returned.
304 308
305 309 Following the map header is a series of 0 or more CBOR values. If values
306 310 are present, the first value will always be a map describing a single changeset
307 311 revision.
308 312
309 313 If the ``fieldsfollowing`` key is present, the map will immediately be followed
310 314 by N CBOR bytestring values, where N is the number of elements in
311 315 ``fieldsfollowing``. Each bytestring value corresponds to a field denoted
312 316 by ``fieldsfollowing``.
313 317
314 318 Following the optional bytestring field values is the next revision descriptor
315 319 map, or end of stream.
316 320
317 321 Each revision descriptor map has the following bytestring keys:
318 322
319 323 Each map has the following bytestring keys:
320 324
321 325 node
322 326 (bytestring) The node of the file revision whose data is represented.
323 327
324 328 deltabasenode
325 329 (bytestring) Node of the file revision the following delta is against.
326 330
327 331 Only present if the ``revision`` field is requested and delta data
328 332 follows this map.
329 333
330 334 fieldsfollowing
331 335 (array of 2-array) Denotes extra bytestring fields that following this map.
332 336 See the documentation for ``changesetdata`` for semantics.
333 337
334 338 The following named fields may be present:
335 339
336 340 ``delta``
337 341 The delta data to use to construct the fulltext revision.
338 342
339 343 Only present if the ``revision`` field is requested and a delta is
340 344 being emitted. The ``deltabasenode`` top-level key will also be
341 345 present if this field is being emitted.
342 346
343 347 ``revision``
344 348 The fulltext revision data for this manifest. Only present if the
345 349 ``revision`` field is requested and a fulltext revision is being emitted.
346 350
347 351 parents
348 352 (array of bytestring) The nodes of the parents of this file revision.
349 353
350 354 Only present if the ``parents`` field is requested.
351 355
352 356 When ``revision`` data is requested, the server chooses to emit either fulltext
353 357 revision data or a delta. What the server decides can be inferred by looking
354 358 for the presence of the ``delta`` or ``revision`` keys in the
355 359 ``fieldsfollowing`` array.
356 360
357 361 heads
358 362 -----
359 363
360 364 Obtain DAG heads in the repository.
361 365
362 366 The command accepts the following arguments:
363 367
364 368 publiconly (optional)
365 369 (boolean) If set, operate on the DAG for public phase changesets only.
366 370 Non-public (i.e. draft) phase DAG heads will not be returned.
367 371
368 372 The response is a CBOR array of bytestrings defining changeset nodes
369 373 of DAG heads. The array can be empty if the repository is empty or no
370 374 changesets satisfied the request.
371 375
372 376 TODO consider exposing phase of heads in response
373 377
374 378 known
375 379 -----
376 380
377 381 Determine whether a series of changeset nodes is known to the server.
378 382
379 383 The command accepts the following arguments:
380 384
381 385 nodes
382 386 (array of bytestrings) List of changeset nodes whose presence to
383 387 query.
384 388
385 389 The response is a bytestring where each byte contains a 0 or 1 for the
386 390 corresponding requested node at the same index.
387 391
388 392 TODO use a bit array for even more compact response
389 393
390 394 listkeys
391 395 --------
392 396
393 397 List values in a specified ``pushkey`` namespace.
394 398
395 399 The command receives the following arguments:
396 400
397 401 namespace
398 402 (bytestring) Pushkey namespace to query.
399 403
400 404 The response is a map with bytestring keys and values.
401 405
402 406 TODO consider using binary to represent nodes in certain pushkey namespaces.
403 407
404 408 lookup
405 409 ------
406 410
407 411 Try to resolve a value to a changeset revision.
408 412
409 413 Unlike ``known`` which operates on changeset nodes, lookup operates on
410 414 node fragments and other names that a user may use.
411 415
412 416 The command receives the following arguments:
413 417
414 418 key
415 419 (bytestring) Value to try to resolve.
416 420
417 421 On success, returns a bytestring containing the resolved node.
418 422
419 423 manifestdata
420 424 ------------
421 425
422 426 Obtain various data related to manifests (which are lists of files in
423 427 a revision).
424 428
425 429 The command accepts the following arguments:
426 430
427 431 fields
428 432 (set of bytestring) Which data associated with manifests to fetch.
429 433 The following values are recognized:
430 434
431 435 parents
432 436 Parent nodes for the manifest.
433 437
434 438 revision
435 439 The raw revision data for the manifest.
436 440
437 441 haveparents
438 442 (bool) Whether the client has the parent revisions of all requested
439 443 nodes. If set, the server may emit revision data as deltas against
440 444 any parent revision. If not set, the server MUST only emit deltas for
441 445 revisions previously emitted by this command.
442 446
443 447 False is assumed in the absence of any value.
444 448
445 449 nodes
446 450 (array of bytestring) Manifest nodes whose data to retrieve.
447 451
448 452 tree
449 453 (bytestring) Path to manifest to retrieve. The empty bytestring represents
450 454 the root manifest. All other values represent directories/trees within
451 455 the repository.
452 456
453 457 TODO allow specifying revisions via alternate means (such as from changeset
454 458 revisions or ranges)
455 459 TODO consider recursive expansion of manifests (with path filtering for
456 460 narrow use cases)
457 461
458 462 The response bytestream starts with a CBOR map describing the data that
459 463 follows. It has the following bytestring keys:
460 464
461 465 totalitems
462 466 (unsigned integer) Total number of manifest revisions whose data is
463 467 being returned.
464 468
465 469 Following the map header is a series of 0 or more CBOR values. If values
466 470 are present, the first value will always be a map describing a single manifest
467 471 revision.
468 472
469 473 If the ``fieldsfollowing`` key is present, the map will immediately be followed
470 474 by N CBOR bytestring values, where N is the number of elements in
471 475 ``fieldsfollowing``. Each bytestring value corresponds to a field denoted
472 476 by ``fieldsfollowing``.
473 477
474 478 Following the optional bytestring field values is the next revision descriptor
475 479 map, or end of stream.
476 480
477 481 Each revision descriptor map has the following bytestring keys:
478 482
479 483 node
480 484 (bytestring) The node of the manifest revision whose data is represented.
481 485
482 486 deltabasenode
483 487 (bytestring) The node that the delta representation of this revision is
484 488 computed against. Only present if the ``revision`` field is requested and
485 489 a delta is being emitted.
486 490
487 491 fieldsfollowing
488 492 (array of 2-array) Denotes extra bytestring fields that following this map.
489 493 See the documentation for ``changesetdata`` for semantics.
490 494
491 495 The following named fields may be present:
492 496
493 497 ``delta``
494 498 The delta data to use to construct the fulltext revision.
495 499
496 500 Only present if the ``revision`` field is requested and a delta is
497 501 being emitted. The ``deltabasenode`` top-level key will also be
498 502 present if this field is being emitted.
499 503
500 504 ``revision``
501 505 The fulltext revision data for this manifest. Only present if the
502 506 ``revision`` field is requested and a fulltext revision is being emitted.
503 507
504 508 parents
505 509 (array of bytestring) The nodes of the parents of this manifest revision.
506 510 Only present if the ``parents`` field is requested.
507 511
508 512 When ``revision`` data is requested, the server chooses to emit either fulltext
509 513 revision data or a delta. What the server decides can be inferred by looking
510 514 for the presence of ``delta`` or ``revision`` in the ``fieldsfollowing`` array.
511 515
516 Servers MAY advertise the following extra fields in the capabilities
517 descriptor for this command:
518
519 recommendedbatchsize
520 (unsigned integer) Number of revisions the server recommends as a batch
521 query size. If defined, clients needing to issue multiple ``manifestdata``
522 commands to obtain needed data SHOULD construct their commands to have
523 this many revisions per request.
524
512 525 pushkey
513 526 -------
514 527
515 528 Set a value using the ``pushkey`` protocol.
516 529
517 530 The command receives the following arguments:
518 531
519 532 namespace
520 533 (bytestring) Pushkey namespace to operate on.
521 534 key
522 535 (bytestring) The pushkey key to set.
523 536 old
524 537 (bytestring) Old value for this key.
525 538 new
526 539 (bytestring) New value for this key.
527 540
528 541 TODO consider using binary to represent nodes is certain pushkey namespaces.
529 542 TODO better define response type and meaning.
@@ -1,390 +1,391 b''
1 1 # Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com>
2 2 #
3 3 # This software may be used and distributed according to the terms of the
4 4 # GNU General Public License version 2 or any later version.
5 5
6 6 from __future__ import absolute_import
7 7
8 8 from .node import (
9 9 bin,
10 10 hex,
11 11 )
12 12 from .i18n import _
13 13 from .thirdparty import (
14 14 attr,
15 15 )
16 16 from . import (
17 17 error,
18 18 util,
19 19 )
20 20 from .utils import (
21 21 interfaceutil,
22 22 )
23 23
24 24 # Names of the SSH protocol implementations.
25 25 SSHV1 = 'ssh-v1'
26 26 # These are advertised over the wire. Increment the counters at the end
27 27 # to reflect BC breakages.
28 28 SSHV2 = 'exp-ssh-v2-0002'
29 29 HTTP_WIREPROTO_V2 = 'exp-http-v2-0002'
30 30
31 31 NARROWCAP = 'exp-narrow-1'
32 32 ELLIPSESCAP = 'exp-ellipses-1'
33 33
34 34 # All available wire protocol transports.
35 35 TRANSPORTS = {
36 36 SSHV1: {
37 37 'transport': 'ssh',
38 38 'version': 1,
39 39 },
40 40 SSHV2: {
41 41 'transport': 'ssh',
42 42 # TODO mark as version 2 once all commands are implemented.
43 43 'version': 1,
44 44 },
45 45 'http-v1': {
46 46 'transport': 'http',
47 47 'version': 1,
48 48 },
49 49 HTTP_WIREPROTO_V2: {
50 50 'transport': 'http',
51 51 'version': 2,
52 52 }
53 53 }
54 54
55 55 class bytesresponse(object):
56 56 """A wire protocol response consisting of raw bytes."""
57 57 def __init__(self, data):
58 58 self.data = data
59 59
60 60 class ooberror(object):
61 61 """wireproto reply: failure of a batch of operation
62 62
63 63 Something failed during a batch call. The error message is stored in
64 64 `self.message`.
65 65 """
66 66 def __init__(self, message):
67 67 self.message = message
68 68
69 69 class pushres(object):
70 70 """wireproto reply: success with simple integer return
71 71
72 72 The call was successful and returned an integer contained in `self.res`.
73 73 """
74 74 def __init__(self, res, output):
75 75 self.res = res
76 76 self.output = output
77 77
78 78 class pusherr(object):
79 79 """wireproto reply: failure
80 80
81 81 The call failed. The `self.res` attribute contains the error message.
82 82 """
83 83 def __init__(self, res, output):
84 84 self.res = res
85 85 self.output = output
86 86
87 87 class streamres(object):
88 88 """wireproto reply: binary stream
89 89
90 90 The call was successful and the result is a stream.
91 91
92 92 Accepts a generator containing chunks of data to be sent to the client.
93 93
94 94 ``prefer_uncompressed`` indicates that the data is expected to be
95 95 uncompressable and that the stream should therefore use the ``none``
96 96 engine.
97 97 """
98 98 def __init__(self, gen=None, prefer_uncompressed=False):
99 99 self.gen = gen
100 100 self.prefer_uncompressed = prefer_uncompressed
101 101
102 102 class streamreslegacy(object):
103 103 """wireproto reply: uncompressed binary stream
104 104
105 105 The call was successful and the result is a stream.
106 106
107 107 Accepts a generator containing chunks of data to be sent to the client.
108 108
109 109 Like ``streamres``, but sends an uncompressed data for "version 1" clients
110 110 using the application/mercurial-0.1 media type.
111 111 """
112 112 def __init__(self, gen=None):
113 113 self.gen = gen
114 114
115 115 # list of nodes encoding / decoding
116 116 def decodelist(l, sep=' '):
117 117 if l:
118 118 return [bin(v) for v in l.split(sep)]
119 119 return []
120 120
121 121 def encodelist(l, sep=' '):
122 122 try:
123 123 return sep.join(map(hex, l))
124 124 except TypeError:
125 125 raise
126 126
127 127 # batched call argument encoding
128 128
129 129 def escapebatcharg(plain):
130 130 return (plain
131 131 .replace(':', ':c')
132 132 .replace(',', ':o')
133 133 .replace(';', ':s')
134 134 .replace('=', ':e'))
135 135
136 136 def unescapebatcharg(escaped):
137 137 return (escaped
138 138 .replace(':e', '=')
139 139 .replace(':s', ';')
140 140 .replace(':o', ',')
141 141 .replace(':c', ':'))
142 142
143 143 # mapping of options accepted by getbundle and their types
144 144 #
145 145 # Meant to be extended by extensions. It is extensions responsibility to ensure
146 146 # such options are properly processed in exchange.getbundle.
147 147 #
148 148 # supported types are:
149 149 #
150 150 # :nodes: list of binary nodes
151 151 # :csv: list of comma-separated values
152 152 # :scsv: list of comma-separated values return as set
153 153 # :plain: string with no transformation needed.
154 154 GETBUNDLE_ARGUMENTS = {
155 155 'heads': 'nodes',
156 156 'bookmarks': 'boolean',
157 157 'common': 'nodes',
158 158 'obsmarkers': 'boolean',
159 159 'phases': 'boolean',
160 160 'bundlecaps': 'scsv',
161 161 'listkeys': 'csv',
162 162 'cg': 'boolean',
163 163 'cbattempted': 'boolean',
164 164 'stream': 'boolean',
165 165 }
166 166
167 167 class baseprotocolhandler(interfaceutil.Interface):
168 168 """Abstract base class for wire protocol handlers.
169 169
170 170 A wire protocol handler serves as an interface between protocol command
171 171 handlers and the wire protocol transport layer. Protocol handlers provide
172 172 methods to read command arguments, redirect stdio for the duration of
173 173 the request, handle response types, etc.
174 174 """
175 175
176 176 name = interfaceutil.Attribute(
177 177 """The name of the protocol implementation.
178 178
179 179 Used for uniquely identifying the transport type.
180 180 """)
181 181
182 182 def getargs(args):
183 183 """return the value for arguments in <args>
184 184
185 185 For version 1 transports, returns a list of values in the same
186 186 order they appear in ``args``. For version 2 transports, returns
187 187 a dict mapping argument name to value.
188 188 """
189 189
190 190 def getprotocaps():
191 191 """Returns the list of protocol-level capabilities of client
192 192
193 193 Returns a list of capabilities as declared by the client for
194 194 the current request (or connection for stateful protocol handlers)."""
195 195
196 196 def getpayload():
197 197 """Provide a generator for the raw payload.
198 198
199 199 The caller is responsible for ensuring that the full payload is
200 200 processed.
201 201 """
202 202
203 203 def mayberedirectstdio():
204 204 """Context manager to possibly redirect stdio.
205 205
206 206 The context manager yields a file-object like object that receives
207 207 stdout and stderr output when the context manager is active. Or it
208 208 yields ``None`` if no I/O redirection occurs.
209 209
210 210 The intent of this context manager is to capture stdio output
211 211 so it may be sent in the response. Some transports support streaming
212 212 stdio to the client in real time. For these transports, stdio output
213 213 won't be captured.
214 214 """
215 215
216 216 def client():
217 217 """Returns a string representation of this client (as bytes)."""
218 218
219 219 def addcapabilities(repo, caps):
220 220 """Adds advertised capabilities specific to this protocol.
221 221
222 222 Receives the list of capabilities collected so far.
223 223
224 224 Returns a list of capabilities. The passed in argument can be returned.
225 225 """
226 226
227 227 def checkperm(perm):
228 228 """Validate that the client has permissions to perform a request.
229 229
230 230 The argument is the permission required to proceed. If the client
231 231 doesn't have that permission, the exception should raise or abort
232 232 in a protocol specific manner.
233 233 """
234 234
235 235 class commandentry(object):
236 236 """Represents a declared wire protocol command."""
237 237 def __init__(self, func, args='', transports=None,
238 permission='push', cachekeyfn=None):
238 permission='push', cachekeyfn=None, extracapabilitiesfn=None):
239 239 self.func = func
240 240 self.args = args
241 241 self.transports = transports or set()
242 242 self.permission = permission
243 243 self.cachekeyfn = cachekeyfn
244 self.extracapabilitiesfn = extracapabilitiesfn
244 245
245 246 def _merge(self, func, args):
246 247 """Merge this instance with an incoming 2-tuple.
247 248
248 249 This is called when a caller using the old 2-tuple API attempts
249 250 to replace an instance. The incoming values are merged with
250 251 data not captured by the 2-tuple and a new instance containing
251 252 the union of the two objects is returned.
252 253 """
253 254 return commandentry(func, args=args, transports=set(self.transports),
254 255 permission=self.permission)
255 256
256 257 # Old code treats instances as 2-tuples. So expose that interface.
257 258 def __iter__(self):
258 259 yield self.func
259 260 yield self.args
260 261
261 262 def __getitem__(self, i):
262 263 if i == 0:
263 264 return self.func
264 265 elif i == 1:
265 266 return self.args
266 267 else:
267 268 raise IndexError('can only access elements 0 and 1')
268 269
269 270 class commanddict(dict):
270 271 """Container for registered wire protocol commands.
271 272
272 273 It behaves like a dict. But __setitem__ is overwritten to allow silent
273 274 coercion of values from 2-tuples for API compatibility.
274 275 """
275 276 def __setitem__(self, k, v):
276 277 if isinstance(v, commandentry):
277 278 pass
278 279 # Cast 2-tuples to commandentry instances.
279 280 elif isinstance(v, tuple):
280 281 if len(v) != 2:
281 282 raise ValueError('command tuples must have exactly 2 elements')
282 283
283 284 # It is common for extensions to wrap wire protocol commands via
284 285 # e.g. ``wireproto.commands[x] = (newfn, args)``. Because callers
285 286 # doing this aren't aware of the new API that uses objects to store
286 287 # command entries, we automatically merge old state with new.
287 288 if k in self:
288 289 v = self[k]._merge(v[0], v[1])
289 290 else:
290 291 # Use default values from @wireprotocommand.
291 292 v = commandentry(v[0], args=v[1],
292 293 transports=set(TRANSPORTS),
293 294 permission='push')
294 295 else:
295 296 raise ValueError('command entries must be commandentry instances '
296 297 'or 2-tuples')
297 298
298 299 return super(commanddict, self).__setitem__(k, v)
299 300
300 301 def commandavailable(self, command, proto):
301 302 """Determine if a command is available for the requested protocol."""
302 303 assert proto.name in TRANSPORTS
303 304
304 305 entry = self.get(command)
305 306
306 307 if not entry:
307 308 return False
308 309
309 310 if proto.name not in entry.transports:
310 311 return False
311 312
312 313 return True
313 314
314 315 def supportedcompengines(ui, role):
315 316 """Obtain the list of supported compression engines for a request."""
316 317 assert role in (util.CLIENTROLE, util.SERVERROLE)
317 318
318 319 compengines = util.compengines.supportedwireengines(role)
319 320
320 321 # Allow config to override default list and ordering.
321 322 if role == util.SERVERROLE:
322 323 configengines = ui.configlist('server', 'compressionengines')
323 324 config = 'server.compressionengines'
324 325 else:
325 326 # This is currently implemented mainly to facilitate testing. In most
326 327 # cases, the server should be in charge of choosing a compression engine
327 328 # because a server has the most to lose from a sub-optimal choice. (e.g.
328 329 # CPU DoS due to an expensive engine or a network DoS due to poor
329 330 # compression ratio).
330 331 configengines = ui.configlist('experimental',
331 332 'clientcompressionengines')
332 333 config = 'experimental.clientcompressionengines'
333 334
334 335 # No explicit config. Filter out the ones that aren't supposed to be
335 336 # advertised and return default ordering.
336 337 if not configengines:
337 338 attr = 'serverpriority' if role == util.SERVERROLE else 'clientpriority'
338 339 return [e for e in compengines
339 340 if getattr(e.wireprotosupport(), attr) > 0]
340 341
341 342 # If compression engines are listed in the config, assume there is a good
342 343 # reason for it (like server operators wanting to achieve specific
343 344 # performance characteristics). So fail fast if the config references
344 345 # unusable compression engines.
345 346 validnames = set(e.name() for e in compengines)
346 347 invalidnames = set(e for e in configengines if e not in validnames)
347 348 if invalidnames:
348 349 raise error.Abort(_('invalid compression engine defined in %s: %s') %
349 350 (config, ', '.join(sorted(invalidnames))))
350 351
351 352 compengines = [e for e in compengines if e.name() in configengines]
352 353 compengines = sorted(compengines,
353 354 key=lambda e: configengines.index(e.name()))
354 355
355 356 if not compengines:
356 357 raise error.Abort(_('%s config option does not specify any known '
357 358 'compression engines') % config,
358 359 hint=_('usable compression engines: %s') %
359 360 ', '.sorted(validnames))
360 361
361 362 return compengines
362 363
363 364 @attr.s
364 365 class encodedresponse(object):
365 366 """Represents response data that is already content encoded.
366 367
367 368 Wire protocol version 2 only.
368 369
369 370 Commands typically emit Python objects that are encoded and sent over the
370 371 wire. If commands emit an object of this type, the encoding step is bypassed
371 372 and the content from this object is used instead.
372 373 """
373 374 data = attr.ib()
374 375
375 376 @attr.s
376 377 class alternatelocationresponse(object):
377 378 """Represents a response available at an alternate location.
378 379
379 380 Instances are sent in place of actual response objects when the server
380 381 is sending a "content redirect" response.
381 382
382 383 Only compatible with wire protocol version 2.
383 384 """
384 385 url = attr.ib()
385 386 mediatype = attr.ib()
386 387 size = attr.ib(default=None)
387 388 fullhashes = attr.ib(default=None)
388 389 fullhashseed = attr.ib(default=None)
389 390 serverdercerts = attr.ib(default=None)
390 391 servercadercerts = attr.ib(default=None)
@@ -1,1192 +1,1212 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 contextlib
10 10 import hashlib
11 11
12 12 from .i18n import _
13 13 from .node import (
14 14 hex,
15 15 nullid,
16 16 )
17 17 from . import (
18 18 discovery,
19 19 encoding,
20 20 error,
21 21 narrowspec,
22 22 pycompat,
23 23 wireprotoframing,
24 24 wireprototypes,
25 25 )
26 26 from .utils import (
27 27 cborutil,
28 28 interfaceutil,
29 29 stringutil,
30 30 )
31 31
32 32 FRAMINGTYPE = b'application/mercurial-exp-framing-0006'
33 33
34 34 HTTP_WIREPROTO_V2 = wireprototypes.HTTP_WIREPROTO_V2
35 35
36 36 COMMANDS = wireprototypes.commanddict()
37 37
38 38 # Value inserted into cache key computation function. Change the value to
39 39 # force new cache keys for every command request. This should be done when
40 40 # there is a change to how caching works, etc.
41 41 GLOBAL_CACHE_VERSION = 1
42 42
43 43 def handlehttpv2request(rctx, req, res, checkperm, urlparts):
44 44 from .hgweb import common as hgwebcommon
45 45
46 46 # URL space looks like: <permissions>/<command>, where <permission> can
47 47 # be ``ro`` or ``rw`` to signal read-only or read-write, respectively.
48 48
49 49 # Root URL does nothing meaningful... yet.
50 50 if not urlparts:
51 51 res.status = b'200 OK'
52 52 res.headers[b'Content-Type'] = b'text/plain'
53 53 res.setbodybytes(_('HTTP version 2 API handler'))
54 54 return
55 55
56 56 if len(urlparts) == 1:
57 57 res.status = b'404 Not Found'
58 58 res.headers[b'Content-Type'] = b'text/plain'
59 59 res.setbodybytes(_('do not know how to process %s\n') %
60 60 req.dispatchpath)
61 61 return
62 62
63 63 permission, command = urlparts[0:2]
64 64
65 65 if permission not in (b'ro', b'rw'):
66 66 res.status = b'404 Not Found'
67 67 res.headers[b'Content-Type'] = b'text/plain'
68 68 res.setbodybytes(_('unknown permission: %s') % permission)
69 69 return
70 70
71 71 if req.method != 'POST':
72 72 res.status = b'405 Method Not Allowed'
73 73 res.headers[b'Allow'] = b'POST'
74 74 res.setbodybytes(_('commands require POST requests'))
75 75 return
76 76
77 77 # At some point we'll want to use our own API instead of recycling the
78 78 # behavior of version 1 of the wire protocol...
79 79 # TODO return reasonable responses - not responses that overload the
80 80 # HTTP status line message for error reporting.
81 81 try:
82 82 checkperm(rctx, req, 'pull' if permission == b'ro' else 'push')
83 83 except hgwebcommon.ErrorResponse as e:
84 84 res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
85 85 for k, v in e.headers:
86 86 res.headers[k] = v
87 87 res.setbodybytes('permission denied')
88 88 return
89 89
90 90 # We have a special endpoint to reflect the request back at the client.
91 91 if command == b'debugreflect':
92 92 _processhttpv2reflectrequest(rctx.repo.ui, rctx.repo, req, res)
93 93 return
94 94
95 95 # Extra commands that we handle that aren't really wire protocol
96 96 # commands. Think extra hard before making this hackery available to
97 97 # extension.
98 98 extracommands = {'multirequest'}
99 99
100 100 if command not in COMMANDS and command not in extracommands:
101 101 res.status = b'404 Not Found'
102 102 res.headers[b'Content-Type'] = b'text/plain'
103 103 res.setbodybytes(_('unknown wire protocol command: %s\n') % command)
104 104 return
105 105
106 106 repo = rctx.repo
107 107 ui = repo.ui
108 108
109 109 proto = httpv2protocolhandler(req, ui)
110 110
111 111 if (not COMMANDS.commandavailable(command, proto)
112 112 and command not in extracommands):
113 113 res.status = b'404 Not Found'
114 114 res.headers[b'Content-Type'] = b'text/plain'
115 115 res.setbodybytes(_('invalid wire protocol command: %s') % command)
116 116 return
117 117
118 118 # TODO consider cases where proxies may add additional Accept headers.
119 119 if req.headers.get(b'Accept') != FRAMINGTYPE:
120 120 res.status = b'406 Not Acceptable'
121 121 res.headers[b'Content-Type'] = b'text/plain'
122 122 res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
123 123 % FRAMINGTYPE)
124 124 return
125 125
126 126 if req.headers.get(b'Content-Type') != FRAMINGTYPE:
127 127 res.status = b'415 Unsupported Media Type'
128 128 # TODO we should send a response with appropriate media type,
129 129 # since client does Accept it.
130 130 res.headers[b'Content-Type'] = b'text/plain'
131 131 res.setbodybytes(_('client MUST send Content-Type header with '
132 132 'value: %s\n') % FRAMINGTYPE)
133 133 return
134 134
135 135 _processhttpv2request(ui, repo, req, res, permission, command, proto)
136 136
137 137 def _processhttpv2reflectrequest(ui, repo, req, res):
138 138 """Reads unified frame protocol request and dumps out state to client.
139 139
140 140 This special endpoint can be used to help debug the wire protocol.
141 141
142 142 Instead of routing the request through the normal dispatch mechanism,
143 143 we instead read all frames, decode them, and feed them into our state
144 144 tracker. We then dump the log of all that activity back out to the
145 145 client.
146 146 """
147 147 import json
148 148
149 149 # Reflection APIs have a history of being abused, accidentally disclosing
150 150 # sensitive data, etc. So we have a config knob.
151 151 if not ui.configbool('experimental', 'web.api.debugreflect'):
152 152 res.status = b'404 Not Found'
153 153 res.headers[b'Content-Type'] = b'text/plain'
154 154 res.setbodybytes(_('debugreflect service not available'))
155 155 return
156 156
157 157 # We assume we have a unified framing protocol request body.
158 158
159 159 reactor = wireprotoframing.serverreactor(ui)
160 160 states = []
161 161
162 162 while True:
163 163 frame = wireprotoframing.readframe(req.bodyfh)
164 164
165 165 if not frame:
166 166 states.append(b'received: <no frame>')
167 167 break
168 168
169 169 states.append(b'received: %d %d %d %s' % (frame.typeid, frame.flags,
170 170 frame.requestid,
171 171 frame.payload))
172 172
173 173 action, meta = reactor.onframerecv(frame)
174 174 states.append(json.dumps((action, meta), sort_keys=True,
175 175 separators=(', ', ': ')))
176 176
177 177 action, meta = reactor.oninputeof()
178 178 meta['action'] = action
179 179 states.append(json.dumps(meta, sort_keys=True, separators=(', ',': ')))
180 180
181 181 res.status = b'200 OK'
182 182 res.headers[b'Content-Type'] = b'text/plain'
183 183 res.setbodybytes(b'\n'.join(states))
184 184
185 185 def _processhttpv2request(ui, repo, req, res, authedperm, reqcommand, proto):
186 186 """Post-validation handler for HTTPv2 requests.
187 187
188 188 Called when the HTTP request contains unified frame-based protocol
189 189 frames for evaluation.
190 190 """
191 191 # TODO Some HTTP clients are full duplex and can receive data before
192 192 # the entire request is transmitted. Figure out a way to indicate support
193 193 # for that so we can opt into full duplex mode.
194 194 reactor = wireprotoframing.serverreactor(ui, deferoutput=True)
195 195 seencommand = False
196 196
197 197 outstream = None
198 198
199 199 while True:
200 200 frame = wireprotoframing.readframe(req.bodyfh)
201 201 if not frame:
202 202 break
203 203
204 204 action, meta = reactor.onframerecv(frame)
205 205
206 206 if action == 'wantframe':
207 207 # Need more data before we can do anything.
208 208 continue
209 209 elif action == 'runcommand':
210 210 # Defer creating output stream because we need to wait for
211 211 # protocol settings frames so proper encoding can be applied.
212 212 if not outstream:
213 213 outstream = reactor.makeoutputstream()
214 214
215 215 sentoutput = _httpv2runcommand(ui, repo, req, res, authedperm,
216 216 reqcommand, reactor, outstream,
217 217 meta, issubsequent=seencommand)
218 218
219 219 if sentoutput:
220 220 return
221 221
222 222 seencommand = True
223 223
224 224 elif action == 'error':
225 225 # TODO define proper error mechanism.
226 226 res.status = b'200 OK'
227 227 res.headers[b'Content-Type'] = b'text/plain'
228 228 res.setbodybytes(meta['message'] + b'\n')
229 229 return
230 230 else:
231 231 raise error.ProgrammingError(
232 232 'unhandled action from frame processor: %s' % action)
233 233
234 234 action, meta = reactor.oninputeof()
235 235 if action == 'sendframes':
236 236 # We assume we haven't started sending the response yet. If we're
237 237 # wrong, the response type will raise an exception.
238 238 res.status = b'200 OK'
239 239 res.headers[b'Content-Type'] = FRAMINGTYPE
240 240 res.setbodygen(meta['framegen'])
241 241 elif action == 'noop':
242 242 pass
243 243 else:
244 244 raise error.ProgrammingError('unhandled action from frame processor: %s'
245 245 % action)
246 246
247 247 def _httpv2runcommand(ui, repo, req, res, authedperm, reqcommand, reactor,
248 248 outstream, command, issubsequent):
249 249 """Dispatch a wire protocol command made from HTTPv2 requests.
250 250
251 251 The authenticated permission (``authedperm``) along with the original
252 252 command from the URL (``reqcommand``) are passed in.
253 253 """
254 254 # We already validated that the session has permissions to perform the
255 255 # actions in ``authedperm``. In the unified frame protocol, the canonical
256 256 # command to run is expressed in a frame. However, the URL also requested
257 257 # to run a specific command. We need to be careful that the command we
258 258 # run doesn't have permissions requirements greater than what was granted
259 259 # by ``authedperm``.
260 260 #
261 261 # Our rule for this is we only allow one command per HTTP request and
262 262 # that command must match the command in the URL. However, we make
263 263 # an exception for the ``multirequest`` URL. This URL is allowed to
264 264 # execute multiple commands. We double check permissions of each command
265 265 # as it is invoked to ensure there is no privilege escalation.
266 266 # TODO consider allowing multiple commands to regular command URLs
267 267 # iff each command is the same.
268 268
269 269 proto = httpv2protocolhandler(req, ui, args=command['args'])
270 270
271 271 if reqcommand == b'multirequest':
272 272 if not COMMANDS.commandavailable(command['command'], proto):
273 273 # TODO proper error mechanism
274 274 res.status = b'200 OK'
275 275 res.headers[b'Content-Type'] = b'text/plain'
276 276 res.setbodybytes(_('wire protocol command not available: %s') %
277 277 command['command'])
278 278 return True
279 279
280 280 # TODO don't use assert here, since it may be elided by -O.
281 281 assert authedperm in (b'ro', b'rw')
282 282 wirecommand = COMMANDS[command['command']]
283 283 assert wirecommand.permission in ('push', 'pull')
284 284
285 285 if authedperm == b'ro' and wirecommand.permission != 'pull':
286 286 # TODO proper error mechanism
287 287 res.status = b'403 Forbidden'
288 288 res.headers[b'Content-Type'] = b'text/plain'
289 289 res.setbodybytes(_('insufficient permissions to execute '
290 290 'command: %s') % command['command'])
291 291 return True
292 292
293 293 # TODO should we also call checkperm() here? Maybe not if we're going
294 294 # to overhaul that API. The granted scope from the URL check should
295 295 # be good enough.
296 296
297 297 else:
298 298 # Don't allow multiple commands outside of ``multirequest`` URL.
299 299 if issubsequent:
300 300 # TODO proper error mechanism
301 301 res.status = b'200 OK'
302 302 res.headers[b'Content-Type'] = b'text/plain'
303 303 res.setbodybytes(_('multiple commands cannot be issued to this '
304 304 'URL'))
305 305 return True
306 306
307 307 if reqcommand != command['command']:
308 308 # TODO define proper error mechanism
309 309 res.status = b'200 OK'
310 310 res.headers[b'Content-Type'] = b'text/plain'
311 311 res.setbodybytes(_('command in frame must match command in URL'))
312 312 return True
313 313
314 314 res.status = b'200 OK'
315 315 res.headers[b'Content-Type'] = FRAMINGTYPE
316 316
317 317 try:
318 318 objs = dispatch(repo, proto, command['command'], command['redirect'])
319 319
320 320 action, meta = reactor.oncommandresponsereadyobjects(
321 321 outstream, command['requestid'], objs)
322 322
323 323 except error.WireprotoCommandError as e:
324 324 action, meta = reactor.oncommanderror(
325 325 outstream, command['requestid'], e.message, e.messageargs)
326 326
327 327 except Exception as e:
328 328 action, meta = reactor.onservererror(
329 329 outstream, command['requestid'],
330 330 _('exception when invoking command: %s') %
331 331 stringutil.forcebytestr(e))
332 332
333 333 if action == 'sendframes':
334 334 res.setbodygen(meta['framegen'])
335 335 return True
336 336 elif action == 'noop':
337 337 return False
338 338 else:
339 339 raise error.ProgrammingError('unhandled event from reactor: %s' %
340 340 action)
341 341
342 342 def getdispatchrepo(repo, proto, command):
343 343 return repo.filtered('served')
344 344
345 345 def dispatch(repo, proto, command, redirect):
346 346 """Run a wire protocol command.
347 347
348 348 Returns an iterable of objects that will be sent to the client.
349 349 """
350 350 repo = getdispatchrepo(repo, proto, command)
351 351
352 352 entry = COMMANDS[command]
353 353 func = entry.func
354 354 spec = entry.args
355 355
356 356 args = proto.getargs(spec)
357 357
358 358 # There is some duplicate boilerplate code here for calling the command and
359 359 # emitting objects. It is either that or a lot of indented code that looks
360 360 # like a pyramid (since there are a lot of code paths that result in not
361 361 # using the cacher).
362 362 callcommand = lambda: func(repo, proto, **pycompat.strkwargs(args))
363 363
364 364 # Request is not cacheable. Don't bother instantiating a cacher.
365 365 if not entry.cachekeyfn:
366 366 for o in callcommand():
367 367 yield o
368 368 return
369 369
370 370 if redirect:
371 371 redirecttargets = redirect[b'targets']
372 372 redirecthashes = redirect[b'hashes']
373 373 else:
374 374 redirecttargets = []
375 375 redirecthashes = []
376 376
377 377 cacher = makeresponsecacher(repo, proto, command, args,
378 378 cborutil.streamencode,
379 379 redirecttargets=redirecttargets,
380 380 redirecthashes=redirecthashes)
381 381
382 382 # But we have no cacher. Do default handling.
383 383 if not cacher:
384 384 for o in callcommand():
385 385 yield o
386 386 return
387 387
388 388 with cacher:
389 389 cachekey = entry.cachekeyfn(repo, proto, cacher, **args)
390 390
391 391 # No cache key or the cacher doesn't like it. Do default handling.
392 392 if cachekey is None or not cacher.setcachekey(cachekey):
393 393 for o in callcommand():
394 394 yield o
395 395 return
396 396
397 397 # Serve it from the cache, if possible.
398 398 cached = cacher.lookup()
399 399
400 400 if cached:
401 401 for o in cached['objs']:
402 402 yield o
403 403 return
404 404
405 405 # Else call the command and feed its output into the cacher, allowing
406 406 # the cacher to buffer/mutate objects as it desires.
407 407 for o in callcommand():
408 408 for o in cacher.onobject(o):
409 409 yield o
410 410
411 411 for o in cacher.onfinished():
412 412 yield o
413 413
414 414 @interfaceutil.implementer(wireprototypes.baseprotocolhandler)
415 415 class httpv2protocolhandler(object):
416 416 def __init__(self, req, ui, args=None):
417 417 self._req = req
418 418 self._ui = ui
419 419 self._args = args
420 420
421 421 @property
422 422 def name(self):
423 423 return HTTP_WIREPROTO_V2
424 424
425 425 def getargs(self, args):
426 426 # First look for args that were passed but aren't registered on this
427 427 # command.
428 428 extra = set(self._args) - set(args)
429 429 if extra:
430 430 raise error.WireprotoCommandError(
431 431 'unsupported argument to command: %s' %
432 432 ', '.join(sorted(extra)))
433 433
434 434 # And look for required arguments that are missing.
435 435 missing = {a for a in args if args[a]['required']} - set(self._args)
436 436
437 437 if missing:
438 438 raise error.WireprotoCommandError(
439 439 'missing required arguments: %s' % ', '.join(sorted(missing)))
440 440
441 441 # Now derive the arguments to pass to the command, taking into
442 442 # account the arguments specified by the client.
443 443 data = {}
444 444 for k, meta in sorted(args.items()):
445 445 # This argument wasn't passed by the client.
446 446 if k not in self._args:
447 447 data[k] = meta['default']()
448 448 continue
449 449
450 450 v = self._args[k]
451 451
452 452 # Sets may be expressed as lists. Silently normalize.
453 453 if meta['type'] == 'set' and isinstance(v, list):
454 454 v = set(v)
455 455
456 456 # TODO consider more/stronger type validation.
457 457
458 458 data[k] = v
459 459
460 460 return data
461 461
462 462 def getprotocaps(self):
463 463 # Protocol capabilities are currently not implemented for HTTP V2.
464 464 return set()
465 465
466 466 def getpayload(self):
467 467 raise NotImplementedError
468 468
469 469 @contextlib.contextmanager
470 470 def mayberedirectstdio(self):
471 471 raise NotImplementedError
472 472
473 473 def client(self):
474 474 raise NotImplementedError
475 475
476 476 def addcapabilities(self, repo, caps):
477 477 return caps
478 478
479 479 def checkperm(self, perm):
480 480 raise NotImplementedError
481 481
482 482 def httpv2apidescriptor(req, repo):
483 483 proto = httpv2protocolhandler(req, repo.ui)
484 484
485 485 return _capabilitiesv2(repo, proto)
486 486
487 487 def _capabilitiesv2(repo, proto):
488 488 """Obtain the set of capabilities for version 2 transports.
489 489
490 490 These capabilities are distinct from the capabilities for version 1
491 491 transports.
492 492 """
493 493 caps = {
494 494 'commands': {},
495 495 'framingmediatypes': [FRAMINGTYPE],
496 496 'pathfilterprefixes': set(narrowspec.VALID_PREFIXES),
497 497 }
498 498
499 499 for command, entry in COMMANDS.items():
500 500 args = {}
501 501
502 502 for arg, meta in entry.args.items():
503 503 args[arg] = {
504 504 # TODO should this be a normalized type using CBOR's
505 505 # terminology?
506 506 b'type': meta['type'],
507 507 b'required': meta['required'],
508 508 }
509 509
510 510 if not meta['required']:
511 511 args[arg][b'default'] = meta['default']()
512 512
513 513 if meta['validvalues']:
514 514 args[arg][b'validvalues'] = meta['validvalues']
515 515
516 516 caps['commands'][command] = {
517 517 'args': args,
518 518 'permissions': [entry.permission],
519 519 }
520 520
521 if entry.extracapabilitiesfn:
522 extracaps = entry.extracapabilitiesfn(repo, proto)
523 caps['commands'][command].update(extracaps)
524
521 525 caps['rawrepoformats'] = sorted(repo.requirements &
522 526 repo.supportedformats)
523 527
524 528 targets = getadvertisedredirecttargets(repo, proto)
525 529 if targets:
526 530 caps[b'redirect'] = {
527 531 b'targets': [],
528 532 b'hashes': [b'sha256', b'sha1'],
529 533 }
530 534
531 535 for target in targets:
532 536 entry = {
533 537 b'name': target['name'],
534 538 b'protocol': target['protocol'],
535 539 b'uris': target['uris'],
536 540 }
537 541
538 542 for key in ('snirequired', 'tlsversions'):
539 543 if key in target:
540 544 entry[key] = target[key]
541 545
542 546 caps[b'redirect'][b'targets'].append(entry)
543 547
544 548 return proto.addcapabilities(repo, caps)
545 549
546 550 def getadvertisedredirecttargets(repo, proto):
547 551 """Obtain a list of content redirect targets.
548 552
549 553 Returns a list containing potential redirect targets that will be
550 554 advertised in capabilities data. Each dict MUST have the following
551 555 keys:
552 556
553 557 name
554 558 The name of this redirect target. This is the identifier clients use
555 559 to refer to a target. It is transferred as part of every command
556 560 request.
557 561
558 562 protocol
559 563 Network protocol used by this target. Typically this is the string
560 564 in front of the ``://`` in a URL. e.g. ``https``.
561 565
562 566 uris
563 567 List of representative URIs for this target. Clients can use the
564 568 URIs to test parsing for compatibility or for ordering preference
565 569 for which target to use.
566 570
567 571 The following optional keys are recognized:
568 572
569 573 snirequired
570 574 Bool indicating if Server Name Indication (SNI) is required to
571 575 connect to this target.
572 576
573 577 tlsversions
574 578 List of bytes indicating which TLS versions are supported by this
575 579 target.
576 580
577 581 By default, clients reflect the target order advertised by servers
578 582 and servers will use the first client-advertised target when picking
579 583 a redirect target. So targets should be advertised in the order the
580 584 server prefers they be used.
581 585 """
582 586 return []
583 587
584 def wireprotocommand(name, args=None, permission='push', cachekeyfn=None):
588 def wireprotocommand(name, args=None, permission='push', cachekeyfn=None,
589 extracapabilitiesfn=None):
585 590 """Decorator to declare a wire protocol command.
586 591
587 592 ``name`` is the name of the wire protocol command being provided.
588 593
589 594 ``args`` is a dict defining arguments accepted by the command. Keys are
590 595 the argument name. Values are dicts with the following keys:
591 596
592 597 ``type``
593 598 The argument data type. Must be one of the following string
594 599 literals: ``bytes``, ``int``, ``list``, ``dict``, ``set``,
595 600 or ``bool``.
596 601
597 602 ``default``
598 603 A callable returning the default value for this argument. If not
599 604 specified, ``None`` will be the default value.
600 605
601 606 ``example``
602 607 An example value for this argument.
603 608
604 609 ``validvalues``
605 610 Set of recognized values for this argument.
606 611
607 612 ``permission`` defines the permission type needed to run this command.
608 613 Can be ``push`` or ``pull``. These roughly map to read-write and read-only,
609 614 respectively. Default is to assume command requires ``push`` permissions
610 615 because otherwise commands not declaring their permissions could modify
611 616 a repository that is supposed to be read-only.
612 617
613 618 ``cachekeyfn`` defines an optional callable that can derive the
614 619 cache key for this request.
615 620
621 ``extracapabilitiesfn`` defines an optional callable that defines extra
622 command capabilities/parameters that are advertised next to the command
623 in the capabilities data structure describing the server. The callable
624 receives as arguments the repository and protocol objects. It returns
625 a dict of extra fields to add to the command descriptor.
626
616 627 Wire protocol commands are generators of objects to be serialized and
617 628 sent to the client.
618 629
619 630 If a command raises an uncaught exception, this will be translated into
620 631 a command error.
621 632
622 633 All commands can opt in to being cacheable by defining a function
623 634 (``cachekeyfn``) that is called to derive a cache key. This function
624 635 receives the same arguments as the command itself plus a ``cacher``
625 636 argument containing the active cacher for the request and returns a bytes
626 637 containing the key in a cache the response to this command may be cached
627 638 under.
628 639 """
629 640 transports = {k for k, v in wireprototypes.TRANSPORTS.items()
630 641 if v['version'] == 2}
631 642
632 643 if permission not in ('push', 'pull'):
633 644 raise error.ProgrammingError('invalid wire protocol permission; '
634 645 'got %s; expected "push" or "pull"' %
635 646 permission)
636 647
637 648 if args is None:
638 649 args = {}
639 650
640 651 if not isinstance(args, dict):
641 652 raise error.ProgrammingError('arguments for version 2 commands '
642 653 'must be declared as dicts')
643 654
644 655 for arg, meta in args.items():
645 656 if arg == '*':
646 657 raise error.ProgrammingError('* argument name not allowed on '
647 658 'version 2 commands')
648 659
649 660 if not isinstance(meta, dict):
650 661 raise error.ProgrammingError('arguments for version 2 commands '
651 662 'must declare metadata as a dict')
652 663
653 664 if 'type' not in meta:
654 665 raise error.ProgrammingError('%s argument for command %s does not '
655 666 'declare type field' % (arg, name))
656 667
657 668 if meta['type'] not in ('bytes', 'int', 'list', 'dict', 'set', 'bool'):
658 669 raise error.ProgrammingError('%s argument for command %s has '
659 670 'illegal type: %s' % (arg, name,
660 671 meta['type']))
661 672
662 673 if 'example' not in meta:
663 674 raise error.ProgrammingError('%s argument for command %s does not '
664 675 'declare example field' % (arg, name))
665 676
666 677 meta['required'] = 'default' not in meta
667 678
668 679 meta.setdefault('default', lambda: None)
669 680 meta.setdefault('validvalues', None)
670 681
671 682 def register(func):
672 683 if name in COMMANDS:
673 684 raise error.ProgrammingError('%s command already registered '
674 685 'for version 2' % name)
675 686
676 687 COMMANDS[name] = wireprototypes.commandentry(
677 688 func, args=args, transports=transports, permission=permission,
678 cachekeyfn=cachekeyfn)
689 cachekeyfn=cachekeyfn, extracapabilitiesfn=extracapabilitiesfn)
679 690
680 691 return func
681 692
682 693 return register
683 694
684 695 def makecommandcachekeyfn(command, localversion=None, allargs=False):
685 696 """Construct a cache key derivation function with common features.
686 697
687 698 By default, the cache key is a hash of:
688 699
689 700 * The command name.
690 701 * A global cache version number.
691 702 * A local cache version number (passed via ``localversion``).
692 703 * All the arguments passed to the command.
693 704 * The media type used.
694 705 * Wire protocol version string.
695 706 * The repository path.
696 707 """
697 708 if not allargs:
698 709 raise error.ProgrammingError('only allargs=True is currently supported')
699 710
700 711 if localversion is None:
701 712 raise error.ProgrammingError('must set localversion argument value')
702 713
703 714 def cachekeyfn(repo, proto, cacher, **args):
704 715 spec = COMMANDS[command]
705 716
706 717 # Commands that mutate the repo can not be cached.
707 718 if spec.permission == 'push':
708 719 return None
709 720
710 721 # TODO config option to disable caching.
711 722
712 723 # Our key derivation strategy is to construct a data structure
713 724 # holding everything that could influence cacheability and to hash
714 725 # the CBOR representation of that. Using CBOR seems like it might
715 726 # be overkill. However, simpler hashing mechanisms are prone to
716 727 # duplicate input issues. e.g. if you just concatenate two values,
717 728 # "foo"+"bar" is identical to "fo"+"obar". Using CBOR provides
718 729 # "padding" between values and prevents these problems.
719 730
720 731 # Seed the hash with various data.
721 732 state = {
722 733 # To invalidate all cache keys.
723 734 b'globalversion': GLOBAL_CACHE_VERSION,
724 735 # More granular cache key invalidation.
725 736 b'localversion': localversion,
726 737 # Cache keys are segmented by command.
727 738 b'command': pycompat.sysbytes(command),
728 739 # Throw in the media type and API version strings so changes
729 740 # to exchange semantics invalid cache.
730 741 b'mediatype': FRAMINGTYPE,
731 742 b'version': HTTP_WIREPROTO_V2,
732 743 # So same requests for different repos don't share cache keys.
733 744 b'repo': repo.root,
734 745 }
735 746
736 747 # The arguments passed to us will have already been normalized.
737 748 # Default values will be set, etc. This is important because it
738 749 # means that it doesn't matter if clients send an explicit argument
739 750 # or rely on the default value: it will all normalize to the same
740 751 # set of arguments on the server and therefore the same cache key.
741 752 #
742 753 # Arguments by their very nature must support being encoded to CBOR.
743 754 # And the CBOR encoder is deterministic. So we hash the arguments
744 755 # by feeding the CBOR of their representation into the hasher.
745 756 if allargs:
746 757 state[b'args'] = pycompat.byteskwargs(args)
747 758
748 759 cacher.adjustcachekeystate(state)
749 760
750 761 hasher = hashlib.sha1()
751 762 for chunk in cborutil.streamencode(state):
752 763 hasher.update(chunk)
753 764
754 765 return pycompat.sysbytes(hasher.hexdigest())
755 766
756 767 return cachekeyfn
757 768
758 769 def makeresponsecacher(repo, proto, command, args, objencoderfn,
759 770 redirecttargets, redirecthashes):
760 771 """Construct a cacher for a cacheable command.
761 772
762 773 Returns an ``iwireprotocolcommandcacher`` instance.
763 774
764 775 Extensions can monkeypatch this function to provide custom caching
765 776 backends.
766 777 """
767 778 return None
768 779
769 780 @wireprotocommand('branchmap', permission='pull')
770 781 def branchmapv2(repo, proto):
771 782 yield {encoding.fromlocal(k): v
772 783 for k, v in repo.branchmap().iteritems()}
773 784
774 785 @wireprotocommand('capabilities', permission='pull')
775 786 def capabilitiesv2(repo, proto):
776 787 yield _capabilitiesv2(repo, proto)
777 788
778 789 @wireprotocommand(
779 790 'changesetdata',
780 791 args={
781 792 'noderange': {
782 793 'type': 'list',
783 794 'default': lambda: None,
784 795 'example': [[b'0123456...'], [b'abcdef...']],
785 796 },
786 797 'nodes': {
787 798 'type': 'list',
788 799 'default': lambda: None,
789 800 'example': [b'0123456...'],
790 801 },
791 802 'nodesdepth': {
792 803 'type': 'int',
793 804 'default': lambda: None,
794 805 'example': 10,
795 806 },
796 807 'fields': {
797 808 'type': 'set',
798 809 'default': set,
799 810 'example': {b'parents', b'revision'},
800 811 'validvalues': {b'bookmarks', b'parents', b'phase', b'revision'},
801 812 },
802 813 },
803 814 permission='pull')
804 815 def changesetdata(repo, proto, noderange, nodes, nodesdepth, fields):
805 816 # TODO look for unknown fields and abort when they can't be serviced.
806 817 # This could probably be validated by dispatcher using validvalues.
807 818
808 819 if noderange is None and nodes is None:
809 820 raise error.WireprotoCommandError(
810 821 'noderange or nodes must be defined')
811 822
812 823 if nodesdepth is not None and nodes is None:
813 824 raise error.WireprotoCommandError(
814 825 'nodesdepth requires the nodes argument')
815 826
816 827 if noderange is not None:
817 828 if len(noderange) != 2:
818 829 raise error.WireprotoCommandError(
819 830 'noderange must consist of 2 elements')
820 831
821 832 if not noderange[1]:
822 833 raise error.WireprotoCommandError(
823 834 'heads in noderange request cannot be empty')
824 835
825 836 cl = repo.changelog
826 837 hasnode = cl.hasnode
827 838
828 839 seen = set()
829 840 outgoing = []
830 841
831 842 if nodes is not None:
832 843 outgoing = [n for n in nodes if hasnode(n)]
833 844
834 845 if nodesdepth:
835 846 outgoing = [cl.node(r) for r in
836 847 repo.revs(b'ancestors(%ln, %d)', outgoing,
837 848 nodesdepth - 1)]
838 849
839 850 seen |= set(outgoing)
840 851
841 852 if noderange is not None:
842 853 if noderange[0]:
843 854 common = [n for n in noderange[0] if hasnode(n)]
844 855 else:
845 856 common = [nullid]
846 857
847 858 for n in discovery.outgoing(repo, common, noderange[1]).missing:
848 859 if n not in seen:
849 860 outgoing.append(n)
850 861 # Don't need to add to seen here because this is the final
851 862 # source of nodes and there should be no duplicates in this
852 863 # list.
853 864
854 865 seen.clear()
855 866 publishing = repo.publishing()
856 867
857 868 if outgoing:
858 869 repo.hook('preoutgoing', throw=True, source='serve')
859 870
860 871 yield {
861 872 b'totalitems': len(outgoing),
862 873 }
863 874
864 875 # The phases of nodes already transferred to the client may have changed
865 876 # since the client last requested data. We send phase-only records
866 877 # for these revisions, if requested.
867 878 if b'phase' in fields and noderange is not None:
868 879 # TODO skip nodes whose phase will be reflected by a node in the
869 880 # outgoing set. This is purely an optimization to reduce data
870 881 # size.
871 882 for node in noderange[0]:
872 883 yield {
873 884 b'node': node,
874 885 b'phase': b'public' if publishing else repo[node].phasestr()
875 886 }
876 887
877 888 nodebookmarks = {}
878 889 for mark, node in repo._bookmarks.items():
879 890 nodebookmarks.setdefault(node, set()).add(mark)
880 891
881 892 # It is already topologically sorted by revision number.
882 893 for node in outgoing:
883 894 d = {
884 895 b'node': node,
885 896 }
886 897
887 898 if b'parents' in fields:
888 899 d[b'parents'] = cl.parents(node)
889 900
890 901 if b'phase' in fields:
891 902 if publishing:
892 903 d[b'phase'] = b'public'
893 904 else:
894 905 ctx = repo[node]
895 906 d[b'phase'] = ctx.phasestr()
896 907
897 908 if b'bookmarks' in fields and node in nodebookmarks:
898 909 d[b'bookmarks'] = sorted(nodebookmarks[node])
899 910 del nodebookmarks[node]
900 911
901 912 followingmeta = []
902 913 followingdata = []
903 914
904 915 if b'revision' in fields:
905 916 revisiondata = cl.revision(node, raw=True)
906 917 followingmeta.append((b'revision', len(revisiondata)))
907 918 followingdata.append(revisiondata)
908 919
909 920 # TODO make it possible for extensions to wrap a function or register
910 921 # a handler to service custom fields.
911 922
912 923 if followingmeta:
913 924 d[b'fieldsfollowing'] = followingmeta
914 925
915 926 yield d
916 927
917 928 for extra in followingdata:
918 929 yield extra
919 930
920 931 # If requested, send bookmarks from nodes that didn't have revision
921 932 # data sent so receiver is aware of any bookmark updates.
922 933 if b'bookmarks' in fields:
923 934 for node, marks in sorted(nodebookmarks.iteritems()):
924 935 yield {
925 936 b'node': node,
926 937 b'bookmarks': sorted(marks),
927 938 }
928 939
929 940 class FileAccessError(Exception):
930 941 """Represents an error accessing a specific file."""
931 942
932 943 def __init__(self, path, msg, args):
933 944 self.path = path
934 945 self.msg = msg
935 946 self.args = args
936 947
937 948 def getfilestore(repo, proto, path):
938 949 """Obtain a file storage object for use with wire protocol.
939 950
940 951 Exists as a standalone function so extensions can monkeypatch to add
941 952 access control.
942 953 """
943 954 # This seems to work even if the file doesn't exist. So catch
944 955 # "empty" files and return an error.
945 956 fl = repo.file(path)
946 957
947 958 if not len(fl):
948 959 raise FileAccessError(path, 'unknown file: %s', (path,))
949 960
950 961 return fl
951 962
952 963 @wireprotocommand(
953 964 'filedata',
954 965 args={
955 966 'haveparents': {
956 967 'type': 'bool',
957 968 'default': lambda: False,
958 969 'example': True,
959 970 },
960 971 'nodes': {
961 972 'type': 'list',
962 973 'example': [b'0123456...'],
963 974 },
964 975 'fields': {
965 976 'type': 'set',
966 977 'default': set,
967 978 'example': {b'parents', b'revision'},
968 979 'validvalues': {b'parents', b'revision'},
969 980 },
970 981 'path': {
971 982 'type': 'bytes',
972 983 'example': b'foo.txt',
973 984 }
974 985 },
975 986 permission='pull',
976 987 # TODO censoring a file revision won't invalidate the cache.
977 988 # Figure out a way to take censoring into account when deriving
978 989 # the cache key.
979 990 cachekeyfn=makecommandcachekeyfn('filedata', 1, allargs=True))
980 991 def filedata(repo, proto, haveparents, nodes, fields, path):
981 992 try:
982 993 # Extensions may wish to access the protocol handler.
983 994 store = getfilestore(repo, proto, path)
984 995 except FileAccessError as e:
985 996 raise error.WireprotoCommandError(e.msg, e.args)
986 997
987 998 # Validate requested nodes.
988 999 for node in nodes:
989 1000 try:
990 1001 store.rev(node)
991 1002 except error.LookupError:
992 1003 raise error.WireprotoCommandError('unknown file node: %s',
993 1004 (hex(node),))
994 1005
995 1006 revisions = store.emitrevisions(nodes,
996 1007 revisiondata=b'revision' in fields,
997 1008 assumehaveparentrevisions=haveparents)
998 1009
999 1010 yield {
1000 1011 b'totalitems': len(nodes),
1001 1012 }
1002 1013
1003 1014 for revision in revisions:
1004 1015 d = {
1005 1016 b'node': revision.node,
1006 1017 }
1007 1018
1008 1019 if b'parents' in fields:
1009 1020 d[b'parents'] = [revision.p1node, revision.p2node]
1010 1021
1011 1022 followingmeta = []
1012 1023 followingdata = []
1013 1024
1014 1025 if b'revision' in fields:
1015 1026 if revision.revision is not None:
1016 1027 followingmeta.append((b'revision', len(revision.revision)))
1017 1028 followingdata.append(revision.revision)
1018 1029 else:
1019 1030 d[b'deltabasenode'] = revision.basenode
1020 1031 followingmeta.append((b'delta', len(revision.delta)))
1021 1032 followingdata.append(revision.delta)
1022 1033
1023 1034 if followingmeta:
1024 1035 d[b'fieldsfollowing'] = followingmeta
1025 1036
1026 1037 yield d
1027 1038
1028 1039 for extra in followingdata:
1029 1040 yield extra
1030 1041
1031 1042 @wireprotocommand(
1032 1043 'heads',
1033 1044 args={
1034 1045 'publiconly': {
1035 1046 'type': 'bool',
1036 1047 'default': lambda: False,
1037 1048 'example': False,
1038 1049 },
1039 1050 },
1040 1051 permission='pull')
1041 1052 def headsv2(repo, proto, publiconly):
1042 1053 if publiconly:
1043 1054 repo = repo.filtered('immutable')
1044 1055
1045 1056 yield repo.heads()
1046 1057
1047 1058 @wireprotocommand(
1048 1059 'known',
1049 1060 args={
1050 1061 'nodes': {
1051 1062 'type': 'list',
1052 1063 'default': list,
1053 1064 'example': [b'deadbeef'],
1054 1065 },
1055 1066 },
1056 1067 permission='pull')
1057 1068 def knownv2(repo, proto, nodes):
1058 1069 result = b''.join(b'1' if n else b'0' for n in repo.known(nodes))
1059 1070 yield result
1060 1071
1061 1072 @wireprotocommand(
1062 1073 'listkeys',
1063 1074 args={
1064 1075 'namespace': {
1065 1076 'type': 'bytes',
1066 1077 'example': b'ns',
1067 1078 },
1068 1079 },
1069 1080 permission='pull')
1070 1081 def listkeysv2(repo, proto, namespace):
1071 1082 keys = repo.listkeys(encoding.tolocal(namespace))
1072 1083 keys = {encoding.fromlocal(k): encoding.fromlocal(v)
1073 1084 for k, v in keys.iteritems()}
1074 1085
1075 1086 yield keys
1076 1087
1077 1088 @wireprotocommand(
1078 1089 'lookup',
1079 1090 args={
1080 1091 'key': {
1081 1092 'type': 'bytes',
1082 1093 'example': b'foo',
1083 1094 },
1084 1095 },
1085 1096 permission='pull')
1086 1097 def lookupv2(repo, proto, key):
1087 1098 key = encoding.tolocal(key)
1088 1099
1089 1100 # TODO handle exception.
1090 1101 node = repo.lookup(key)
1091 1102
1092 1103 yield node
1093 1104
1105 def manifestdatacapabilities(repo, proto):
1106 batchsize = repo.ui.configint(
1107 b'experimental', b'server.manifestdata.recommended-batch-size')
1108
1109 return {
1110 b'recommendedbatchsize': batchsize,
1111 }
1112
1094 1113 @wireprotocommand(
1095 1114 'manifestdata',
1096 1115 args={
1097 1116 'nodes': {
1098 1117 'type': 'list',
1099 1118 'example': [b'0123456...'],
1100 1119 },
1101 1120 'haveparents': {
1102 1121 'type': 'bool',
1103 1122 'default': lambda: False,
1104 1123 'example': True,
1105 1124 },
1106 1125 'fields': {
1107 1126 'type': 'set',
1108 1127 'default': set,
1109 1128 'example': {b'parents', b'revision'},
1110 1129 'validvalues': {b'parents', b'revision'},
1111 1130 },
1112 1131 'tree': {
1113 1132 'type': 'bytes',
1114 1133 'example': b'',
1115 1134 },
1116 1135 },
1117 1136 permission='pull',
1118 cachekeyfn=makecommandcachekeyfn('manifestdata', 1, allargs=True))
1137 cachekeyfn=makecommandcachekeyfn('manifestdata', 1, allargs=True),
1138 extracapabilitiesfn=manifestdatacapabilities)
1119 1139 def manifestdata(repo, proto, haveparents, nodes, fields, tree):
1120 1140 store = repo.manifestlog.getstorage(tree)
1121 1141
1122 1142 # Validate the node is known and abort on unknown revisions.
1123 1143 for node in nodes:
1124 1144 try:
1125 1145 store.rev(node)
1126 1146 except error.LookupError:
1127 1147 raise error.WireprotoCommandError(
1128 1148 'unknown node: %s', (node,))
1129 1149
1130 1150 revisions = store.emitrevisions(nodes,
1131 1151 revisiondata=b'revision' in fields,
1132 1152 assumehaveparentrevisions=haveparents)
1133 1153
1134 1154 yield {
1135 1155 b'totalitems': len(nodes),
1136 1156 }
1137 1157
1138 1158 for revision in revisions:
1139 1159 d = {
1140 1160 b'node': revision.node,
1141 1161 }
1142 1162
1143 1163 if b'parents' in fields:
1144 1164 d[b'parents'] = [revision.p1node, revision.p2node]
1145 1165
1146 1166 followingmeta = []
1147 1167 followingdata = []
1148 1168
1149 1169 if b'revision' in fields:
1150 1170 if revision.revision is not None:
1151 1171 followingmeta.append((b'revision', len(revision.revision)))
1152 1172 followingdata.append(revision.revision)
1153 1173 else:
1154 1174 d[b'deltabasenode'] = revision.basenode
1155 1175 followingmeta.append((b'delta', len(revision.delta)))
1156 1176 followingdata.append(revision.delta)
1157 1177
1158 1178 if followingmeta:
1159 1179 d[b'fieldsfollowing'] = followingmeta
1160 1180
1161 1181 yield d
1162 1182
1163 1183 for extra in followingdata:
1164 1184 yield extra
1165 1185
1166 1186 @wireprotocommand(
1167 1187 'pushkey',
1168 1188 args={
1169 1189 'namespace': {
1170 1190 'type': 'bytes',
1171 1191 'example': b'ns',
1172 1192 },
1173 1193 'key': {
1174 1194 'type': 'bytes',
1175 1195 'example': b'key',
1176 1196 },
1177 1197 'old': {
1178 1198 'type': 'bytes',
1179 1199 'example': b'old',
1180 1200 },
1181 1201 'new': {
1182 1202 'type': 'bytes',
1183 1203 'example': 'new',
1184 1204 },
1185 1205 },
1186 1206 permission='push')
1187 1207 def pushkeyv2(repo, proto, namespace, key, old, new):
1188 1208 # TODO handle ui output redirection
1189 1209 yield repo.pushkey(encoding.tolocal(namespace),
1190 1210 encoding.tolocal(key),
1191 1211 encoding.tolocal(old),
1192 1212 encoding.tolocal(new))
@@ -1,759 +1,759 b''
1 1 #require no-chg
2 2
3 3 $ . $TESTDIR/wireprotohelpers.sh
4 4
5 5 $ cat >> $HGRCPATH << EOF
6 6 > [web]
7 7 > push_ssl = false
8 8 > allow_push = *
9 9 > EOF
10 10
11 11 $ hg init server
12 12 $ cd server
13 13 $ touch a
14 14 $ hg -q commit -A -m initial
15 15 $ cd ..
16 16
17 17 $ hg serve -R server -p $HGPORT -d --pid-file hg.pid
18 18 $ cat hg.pid >> $DAEMON_PIDS
19 19
20 20 compression formats are advertised in compression capability
21 21
22 22 #if zstd
23 23 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=zstd,zlib$' > /dev/null
24 24 #else
25 25 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=zlib$' > /dev/null
26 26 #endif
27 27
28 28 $ killdaemons.py
29 29
30 30 server.compressionengines can replace engines list wholesale
31 31
32 32 $ hg serve --config server.compressionengines=none -R server -p $HGPORT -d --pid-file hg.pid
33 33 $ cat hg.pid > $DAEMON_PIDS
34 34 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=none$' > /dev/null
35 35
36 36 $ killdaemons.py
37 37
38 38 Order of engines can also change
39 39
40 40 $ hg serve --config server.compressionengines=none,zlib -R server -p $HGPORT -d --pid-file hg.pid
41 41 $ cat hg.pid > $DAEMON_PIDS
42 42 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities' | tr ' ' '\n' | grep '^compression=none,zlib$' > /dev/null
43 43
44 44 $ killdaemons.py
45 45
46 46 Start a default server again
47 47
48 48 $ hg serve -R server -p $HGPORT -d --pid-file hg.pid
49 49 $ cat hg.pid > $DAEMON_PIDS
50 50
51 51 Server should send application/mercurial-0.1 to clients if no Accept is used
52 52
53 53 $ get-with-headers.py --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
54 54 200 Script output follows
55 55 content-type: application/mercurial-0.1
56 56 date: $HTTP_DATE$
57 57 server: testing stub value
58 58 transfer-encoding: chunked
59 59
60 60 Server should send application/mercurial-0.1 when client says it wants it
61 61
62 62 $ get-with-headers.py --hgproto '0.1' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
63 63 200 Script output follows
64 64 content-type: application/mercurial-0.1
65 65 date: $HTTP_DATE$
66 66 server: testing stub value
67 67 transfer-encoding: chunked
68 68
69 69 Server should send application/mercurial-0.2 when client says it wants it
70 70
71 71 $ get-with-headers.py --hgproto '0.2' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
72 72 200 Script output follows
73 73 content-type: application/mercurial-0.2
74 74 date: $HTTP_DATE$
75 75 server: testing stub value
76 76 transfer-encoding: chunked
77 77
78 78 $ get-with-headers.py --hgproto '0.1 0.2' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
79 79 200 Script output follows
80 80 content-type: application/mercurial-0.2
81 81 date: $HTTP_DATE$
82 82 server: testing stub value
83 83 transfer-encoding: chunked
84 84
85 85 Requesting a compression format that server doesn't support results will fall back to 0.1
86 86
87 87 $ get-with-headers.py --hgproto '0.2 comp=aa' --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' -
88 88 200 Script output follows
89 89 content-type: application/mercurial-0.1
90 90 date: $HTTP_DATE$
91 91 server: testing stub value
92 92 transfer-encoding: chunked
93 93
94 94 #if zstd
95 95 zstd is used if available
96 96
97 97 $ get-with-headers.py --hgproto '0.2 comp=zstd' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
98 98 $ f --size --hexdump --bytes 36 --sha1 resp
99 99 resp: size=248, sha1=4d8d8f87fb82bd542ce52881fdc94f850748
100 100 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
101 101 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 73 74 64 |t follows...zstd|
102 102 0020: 28 b5 2f fd |(./.|
103 103
104 104 #endif
105 105
106 106 application/mercurial-0.2 is not yet used on non-streaming responses
107 107
108 108 $ get-with-headers.py --hgproto '0.2' $LOCALIP:$HGPORT '?cmd=heads' -
109 109 200 Script output follows
110 110 content-length: 41
111 111 content-type: application/mercurial-0.1
112 112 date: $HTTP_DATE$
113 113 server: testing stub value
114 114
115 115 e93700bd72895c5addab234c56d4024b487a362f
116 116
117 117 Now test protocol preference usage
118 118
119 119 $ killdaemons.py
120 120 $ hg serve --config server.compressionengines=none,zlib -R server -p $HGPORT -d --pid-file hg.pid
121 121 $ cat hg.pid > $DAEMON_PIDS
122 122
123 123 No Accept will send 0.1+zlib, even though "none" is preferred b/c "none" isn't supported on 0.1
124 124
125 125 $ get-with-headers.py --headeronly $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' Content-Type
126 126 200 Script output follows
127 127 content-type: application/mercurial-0.1
128 128
129 129 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
130 130 $ f --size --hexdump --bytes 28 --sha1 resp
131 131 resp: size=227, sha1=35a4c074da74f32f5440da3cbf04
132 132 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
133 133 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 78 |t follows..x|
134 134
135 135 Explicit 0.1 will send zlib because "none" isn't supported on 0.1
136 136
137 137 $ get-with-headers.py --hgproto '0.1' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
138 138 $ f --size --hexdump --bytes 28 --sha1 resp
139 139 resp: size=227, sha1=35a4c074da74f32f5440da3cbf04
140 140 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
141 141 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 78 |t follows..x|
142 142
143 143 0.2 with no compression will get "none" because that is server's preference
144 144 (spec says ZL and UN are implicitly supported)
145 145
146 146 $ get-with-headers.py --hgproto '0.2' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
147 147 $ f --size --hexdump --bytes 32 --sha1 resp
148 148 resp: size=432, sha1=ac931b412ec185a02e0e5bcff98dac83
149 149 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
150 150 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 6e 6f 6e 65 |t follows...none|
151 151
152 152 Client receives server preference even if local order doesn't match
153 153
154 154 $ get-with-headers.py --hgproto '0.2 comp=zlib,none' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
155 155 $ f --size --hexdump --bytes 32 --sha1 resp
156 156 resp: size=432, sha1=ac931b412ec185a02e0e5bcff98dac83
157 157 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
158 158 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 6e 6f 6e 65 |t follows...none|
159 159
160 160 Client receives only supported format even if not server preferred format
161 161
162 162 $ get-with-headers.py --hgproto '0.2 comp=zlib' $LOCALIP:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp
163 163 $ f --size --hexdump --bytes 33 --sha1 resp
164 164 resp: size=232, sha1=a1c727f0c9693ca15742a75c30419bc36
165 165 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
166 166 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 6c 69 62 |t follows...zlib|
167 167 0020: 78 |x|
168 168
169 169 $ killdaemons.py
170 170 $ cd ..
171 171
172 172 Test listkeys for listing namespaces
173 173
174 174 $ hg init empty
175 175 $ hg -R empty serve -p $HGPORT -d --pid-file hg.pid
176 176 $ cat hg.pid > $DAEMON_PIDS
177 177
178 178 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
179 179 > command listkeys
180 180 > namespace namespaces
181 181 > EOF
182 182 s> GET /?cmd=capabilities HTTP/1.1\r\n
183 183 s> Accept-Encoding: identity\r\n
184 184 s> accept: application/mercurial-0.1\r\n
185 185 s> host: $LOCALIP:$HGPORT\r\n (glob)
186 186 s> user-agent: Mercurial debugwireproto\r\n
187 187 s> \r\n
188 188 s> makefile('rb', None)
189 189 s> HTTP/1.1 200 Script output follows\r\n
190 190 s> Server: testing stub value\r\n
191 191 s> Date: $HTTP_DATE$\r\n
192 192 s> Content-Type: application/mercurial-0.1\r\n
193 193 s> Content-Length: *\r\n (glob)
194 194 s> \r\n
195 195 s> batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
196 196 sending listkeys command
197 197 s> GET /?cmd=listkeys HTTP/1.1\r\n
198 198 s> Accept-Encoding: identity\r\n
199 199 s> vary: X-HgArg-1,X-HgProto-1\r\n
200 200 s> x-hgarg-1: namespace=namespaces\r\n
201 201 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
202 202 s> accept: application/mercurial-0.1\r\n
203 203 s> host: $LOCALIP:$HGPORT\r\n (glob)
204 204 s> user-agent: Mercurial debugwireproto\r\n
205 205 s> \r\n
206 206 s> makefile('rb', None)
207 207 s> HTTP/1.1 200 Script output follows\r\n
208 208 s> Server: testing stub value\r\n
209 209 s> Date: $HTTP_DATE$\r\n
210 210 s> Content-Type: application/mercurial-0.1\r\n
211 211 s> Content-Length: 30\r\n
212 212 s> \r\n
213 213 s> bookmarks\t\n
214 214 s> namespaces\t\n
215 215 s> phases\t
216 216 response: {
217 217 b'bookmarks': b'',
218 218 b'namespaces': b'',
219 219 b'phases': b''
220 220 }
221 221 (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob)
222 222
223 223 Same thing, but with "httprequest" command
224 224
225 225 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
226 226 > httprequest GET ?cmd=listkeys
227 227 > user-agent: test
228 228 > x-hgarg-1: namespace=namespaces
229 229 > EOF
230 230 using raw connection to peer
231 231 s> GET /?cmd=listkeys HTTP/1.1\r\n
232 232 s> Accept-Encoding: identity\r\n
233 233 s> user-agent: test\r\n
234 234 s> x-hgarg-1: namespace=namespaces\r\n
235 235 s> host: $LOCALIP:$HGPORT\r\n (glob)
236 236 s> \r\n
237 237 s> makefile('rb', None)
238 238 s> HTTP/1.1 200 Script output follows\r\n
239 239 s> Server: testing stub value\r\n
240 240 s> Date: $HTTP_DATE$\r\n
241 241 s> Content-Type: application/mercurial-0.1\r\n
242 242 s> Content-Length: 30\r\n
243 243 s> \r\n
244 244 s> bookmarks\t\n
245 245 s> namespaces\t\n
246 246 s> phases\t
247 247
248 248 Client with HTTPv2 enabled advertises that and gets old capabilities response from old server
249 249
250 250 $ hg --config experimental.httppeer.advertise-v2=true --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
251 251 > command heads
252 252 > EOF
253 253 s> GET /?cmd=capabilities HTTP/1.1\r\n
254 254 s> Accept-Encoding: identity\r\n
255 255 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
256 256 s> x-hgproto-1: cbor\r\n
257 257 s> x-hgupgrade-1: exp-http-v2-0002\r\n
258 258 s> accept: application/mercurial-0.1\r\n
259 259 s> host: $LOCALIP:$HGPORT\r\n (glob)
260 260 s> user-agent: Mercurial debugwireproto\r\n
261 261 s> \r\n
262 262 s> makefile('rb', None)
263 263 s> HTTP/1.1 200 Script output follows\r\n
264 264 s> Server: testing stub value\r\n
265 265 s> Date: $HTTP_DATE$\r\n
266 266 s> Content-Type: application/mercurial-0.1\r\n
267 267 s> Content-Length: *\r\n (glob)
268 268 s> \r\n
269 269 s> batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
270 270 sending heads command
271 271 s> GET /?cmd=heads HTTP/1.1\r\n
272 272 s> Accept-Encoding: identity\r\n
273 273 s> vary: X-HgProto-1\r\n
274 274 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
275 275 s> accept: application/mercurial-0.1\r\n
276 276 s> host: $LOCALIP:$HGPORT\r\n (glob)
277 277 s> user-agent: Mercurial debugwireproto\r\n
278 278 s> \r\n
279 279 s> makefile('rb', None)
280 280 s> HTTP/1.1 200 Script output follows\r\n
281 281 s> Server: testing stub value\r\n
282 282 s> Date: $HTTP_DATE$\r\n
283 283 s> Content-Type: application/mercurial-0.1\r\n
284 284 s> Content-Length: 41\r\n
285 285 s> \r\n
286 286 s> 0000000000000000000000000000000000000000\n
287 287 response: [
288 288 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
289 289 ]
290 290 (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob)
291 291
292 292 $ killdaemons.py
293 293 $ enablehttpv2 empty
294 294 $ hg --config server.compressionengines=zlib -R empty serve -p $HGPORT -d --pid-file hg.pid
295 295 $ cat hg.pid > $DAEMON_PIDS
296 296
297 297 Client with HTTPv2 enabled automatically upgrades if the server supports it
298 298
299 299 $ hg --config experimental.httppeer.advertise-v2=true --config experimental.httppeer.v2-encoder-order=identity --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
300 300 > command heads
301 301 > EOF
302 302 s> GET /?cmd=capabilities HTTP/1.1\r\n
303 303 s> Accept-Encoding: identity\r\n
304 304 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
305 305 s> x-hgproto-1: cbor\r\n
306 306 s> x-hgupgrade-1: exp-http-v2-0002\r\n
307 307 s> accept: application/mercurial-0.1\r\n
308 308 s> host: $LOCALIP:$HGPORT\r\n (glob)
309 309 s> user-agent: Mercurial debugwireproto\r\n
310 310 s> \r\n
311 311 s> makefile('rb', None)
312 312 s> HTTP/1.1 200 OK\r\n
313 313 s> Server: testing stub value\r\n
314 314 s> Date: $HTTP_DATE$\r\n
315 315 s> Content-Type: application/mercurial-cbor\r\n
316 316 s> Content-Length: *\r\n (glob)
317 317 s> \r\n
318 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa4Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
318 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa4Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa3Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullTrecommendedbatchsize\x1a\x00\x01\x86\xa0Gpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
319 319 sending heads command
320 320 s> POST /api/exp-http-v2-0002/ro/heads HTTP/1.1\r\n
321 321 s> Accept-Encoding: identity\r\n
322 322 s> accept: application/mercurial-exp-framing-0006\r\n
323 323 s> content-type: application/mercurial-exp-framing-0006\r\n
324 324 s> content-length: 56\r\n
325 325 s> host: $LOCALIP:$HGPORT\r\n (glob)
326 326 s> user-agent: Mercurial debugwireproto\r\n
327 327 s> \r\n
328 328 s> \x1c\x00\x00\x01\x00\x01\x01\x82\xa1Pcontentencodings\x81Hidentity\x0c\x00\x00\x01\x00\x01\x00\x11\xa1DnameEheads
329 329 s> makefile('rb', None)
330 330 s> HTTP/1.1 200 OK\r\n
331 331 s> Server: testing stub value\r\n
332 332 s> Date: $HTTP_DATE$\r\n
333 333 s> Content-Type: application/mercurial-exp-framing-0006\r\n
334 334 s> Transfer-Encoding: chunked\r\n
335 335 s> \r\n
336 336 s> 11\r\n
337 337 s> \t\x00\x00\x01\x00\x02\x01\x92
338 338 s> Hidentity
339 339 s> \r\n
340 340 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
341 341 s> 13\r\n
342 342 s> \x0b\x00\x00\x01\x00\x02\x041
343 343 s> \xa1FstatusBok
344 344 s> \r\n
345 345 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
346 346 s> 1e\r\n
347 347 s> \x16\x00\x00\x01\x00\x02\x041
348 348 s> \x81T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
349 349 s> \r\n
350 350 received frame(size=22; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
351 351 s> 8\r\n
352 352 s> \x00\x00\x00\x01\x00\x02\x002
353 353 s> \r\n
354 354 s> 0\r\n
355 355 s> \r\n
356 356 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
357 357 response: [
358 358 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
359 359 ]
360 360 (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob)
361 361
362 362 $ killdaemons.py
363 363
364 364 HTTP client follows HTTP redirect on handshake to new repo
365 365
366 366 $ cd $TESTTMP
367 367
368 368 $ hg init redirector
369 369 $ hg init redirected
370 370 $ cd redirected
371 371 $ touch foo
372 372 $ hg -q commit -A -m initial
373 373 $ cd ..
374 374
375 375 $ cat > paths.conf << EOF
376 376 > [paths]
377 377 > / = $TESTTMP/*
378 378 > EOF
379 379
380 380 $ cat > redirectext.py << EOF
381 381 > from mercurial import extensions, wireprotoserver
382 382 > def wrappedcallhttp(orig, repo, req, res, proto, cmd):
383 383 > path = req.advertisedurl[len(req.advertisedbaseurl):]
384 384 > if not path.startswith(b'/redirector'):
385 385 > return orig(repo, req, res, proto, cmd)
386 386 > relpath = path[len(b'/redirector'):]
387 387 > res.status = b'301 Redirect'
388 388 > newurl = b'%s/redirected%s' % (req.baseurl, relpath)
389 389 > if not repo.ui.configbool('testing', 'redirectqs', True) and b'?' in newurl:
390 390 > newurl = newurl[0:newurl.index(b'?')]
391 391 > res.headers[b'Location'] = newurl
392 392 > res.headers[b'Content-Type'] = b'text/plain'
393 393 > res.setbodybytes(b'redirected')
394 394 > return True
395 395 >
396 396 > extensions.wrapfunction(wireprotoserver, '_callhttp', wrappedcallhttp)
397 397 > EOF
398 398
399 399 $ hg --config extensions.redirect=$TESTTMP/redirectext.py \
400 400 > --config server.compressionengines=zlib \
401 401 > serve --web-conf paths.conf --pid-file hg.pid -p $HGPORT -d
402 402 $ cat hg.pid > $DAEMON_PIDS
403 403
404 404 Verify our HTTP 301 is served properly
405 405
406 406 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
407 407 > httprequest GET /redirector?cmd=capabilities
408 408 > user-agent: test
409 409 > EOF
410 410 using raw connection to peer
411 411 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
412 412 s> Accept-Encoding: identity\r\n
413 413 s> user-agent: test\r\n
414 414 s> host: $LOCALIP:$HGPORT\r\n (glob)
415 415 s> \r\n
416 416 s> makefile('rb', None)
417 417 s> HTTP/1.1 301 Redirect\r\n
418 418 s> Server: testing stub value\r\n
419 419 s> Date: $HTTP_DATE$\r\n
420 420 s> Location: http://$LOCALIP:$HGPORT/redirected?cmd=capabilities\r\n (glob)
421 421 s> Content-Type: text/plain\r\n
422 422 s> Content-Length: 10\r\n
423 423 s> \r\n
424 424 s> redirected
425 425 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
426 426 s> Accept-Encoding: identity\r\n
427 427 s> user-agent: test\r\n
428 428 s> host: $LOCALIP:$HGPORT\r\n (glob)
429 429 s> \r\n
430 430 s> makefile('rb', None)
431 431 s> HTTP/1.1 200 Script output follows\r\n
432 432 s> Server: testing stub value\r\n
433 433 s> Date: $HTTP_DATE$\r\n
434 434 s> Content-Type: application/mercurial-0.1\r\n
435 435 s> Content-Length: 467\r\n
436 436 s> \r\n
437 437 s> batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
438 438
439 439 Test with the HTTP peer
440 440
441 441 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT/redirector << EOF
442 442 > command heads
443 443 > EOF
444 444 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
445 445 s> Accept-Encoding: identity\r\n
446 446 s> accept: application/mercurial-0.1\r\n
447 447 s> host: $LOCALIP:$HGPORT\r\n (glob)
448 448 s> user-agent: Mercurial debugwireproto\r\n
449 449 s> \r\n
450 450 s> makefile('rb', None)
451 451 s> HTTP/1.1 301 Redirect\r\n
452 452 s> Server: testing stub value\r\n
453 453 s> Date: $HTTP_DATE$\r\n
454 454 s> Location: http://$LOCALIP:$HGPORT/redirected?cmd=capabilities\r\n (glob)
455 455 s> Content-Type: text/plain\r\n
456 456 s> Content-Length: 10\r\n
457 457 s> \r\n
458 458 s> redirected
459 459 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
460 460 s> Accept-Encoding: identity\r\n
461 461 s> accept: application/mercurial-0.1\r\n
462 462 s> host: $LOCALIP:$HGPORT\r\n (glob)
463 463 s> user-agent: Mercurial debugwireproto\r\n
464 464 s> \r\n
465 465 s> makefile('rb', None)
466 466 s> HTTP/1.1 200 Script output follows\r\n
467 467 s> Server: testing stub value\r\n
468 468 s> Date: $HTTP_DATE$\r\n
469 469 s> Content-Type: application/mercurial-0.1\r\n
470 470 s> Content-Length: 467\r\n
471 471 s> \r\n
472 472 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
473 473 s> batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
474 474 sending heads command
475 475 s> GET /redirected?cmd=heads HTTP/1.1\r\n
476 476 s> Accept-Encoding: identity\r\n
477 477 s> vary: X-HgProto-1\r\n
478 478 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
479 479 s> accept: application/mercurial-0.1\r\n
480 480 s> host: $LOCALIP:$HGPORT\r\n (glob)
481 481 s> user-agent: Mercurial debugwireproto\r\n
482 482 s> \r\n
483 483 s> makefile('rb', None)
484 484 s> HTTP/1.1 200 Script output follows\r\n
485 485 s> Server: testing stub value\r\n
486 486 s> Date: $HTTP_DATE$\r\n
487 487 s> Content-Type: application/mercurial-0.1\r\n
488 488 s> Content-Length: 41\r\n
489 489 s> \r\n
490 490 s> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n
491 491 response: [
492 492 b'\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL'
493 493 ]
494 494 (sent 3 HTTP requests and * bytes; received * bytes in responses) (glob)
495 495
496 496 $ killdaemons.py
497 497
498 498 Now test a variation where we strip the query string from the redirect URL.
499 499 (SCM Manager apparently did this and clients would recover from it)
500 500
501 501 $ hg --config extensions.redirect=$TESTTMP/redirectext.py \
502 502 > --config server.compressionengines=zlib \
503 503 > --config testing.redirectqs=false \
504 504 > serve --web-conf paths.conf --pid-file hg.pid -p $HGPORT -d
505 505 $ cat hg.pid > $DAEMON_PIDS
506 506
507 507 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
508 508 > httprequest GET /redirector?cmd=capabilities
509 509 > user-agent: test
510 510 > EOF
511 511 using raw connection to peer
512 512 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
513 513 s> Accept-Encoding: identity\r\n
514 514 s> user-agent: test\r\n
515 515 s> host: $LOCALIP:$HGPORT\r\n (glob)
516 516 s> \r\n
517 517 s> makefile('rb', None)
518 518 s> HTTP/1.1 301 Redirect\r\n
519 519 s> Server: testing stub value\r\n
520 520 s> Date: $HTTP_DATE$\r\n
521 521 s> Location: http://$LOCALIP:$HGPORT/redirected\r\n (glob)
522 522 s> Content-Type: text/plain\r\n
523 523 s> Content-Length: 10\r\n
524 524 s> \r\n
525 525 s> redirected
526 526 s> GET /redirected HTTP/1.1\r\n
527 527 s> Accept-Encoding: identity\r\n
528 528 s> user-agent: test\r\n
529 529 s> host: $LOCALIP:$HGPORT\r\n (glob)
530 530 s> \r\n
531 531 s> makefile('rb', None)
532 532 s> HTTP/1.1 200 Script output follows\r\n
533 533 s> Server: testing stub value\r\n
534 534 s> Date: $HTTP_DATE$\r\n
535 535 s> ETag: W/"*"\r\n (glob)
536 536 s> Content-Type: text/html; charset=ascii\r\n
537 537 s> Transfer-Encoding: chunked\r\n
538 538 s> \r\n
539 539 s> 414\r\n
540 540 s> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n
541 541 s> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">\n
542 542 s> <head>\n
543 543 s> <link rel="icon" href="/redirected/static/hgicon.png" type="image/png" />\n
544 544 s> <meta name="robots" content="index, nofollow" />\n
545 545 s> <link rel="stylesheet" href="/redirected/static/style-paper.css" type="text/css" />\n
546 546 s> <script type="text/javascript" src="/redirected/static/mercurial.js"></script>\n
547 547 s> \n
548 548 s> <title>redirected: log</title>\n
549 549 s> <link rel="alternate" type="application/atom+xml"\n
550 550 s> href="/redirected/atom-log" title="Atom feed for redirected" />\n
551 551 s> <link rel="alternate" type="application/rss+xml"\n
552 552 s> href="/redirected/rss-log" title="RSS feed for redirected" />\n
553 553 s> </head>\n
554 554 s> <body>\n
555 555 s> \n
556 556 s> <div class="container">\n
557 557 s> <div class="menu">\n
558 558 s> <div class="logo">\n
559 559 s> <a href="https://mercurial-scm.org/">\n
560 560 s> <img src="/redirected/static/hglogo.png" alt="mercurial" /></a>\n
561 561 s> </div>\n
562 562 s> <ul>\n
563 563 s> <li class="active">log</li>\n
564 564 s> <li><a href="/redirected/graph/tip">graph</a></li>\n
565 565 s> <li><a href="/redirected/tags">tags</a></li>\n
566 566 s> <li><a href="
567 567 s> \r\n
568 568 s> 810\r\n
569 569 s> /redirected/bookmarks">bookmarks</a></li>\n
570 570 s> <li><a href="/redirected/branches">branches</a></li>\n
571 571 s> </ul>\n
572 572 s> <ul>\n
573 573 s> <li><a href="/redirected/rev/tip">changeset</a></li>\n
574 574 s> <li><a href="/redirected/file/tip">browse</a></li>\n
575 575 s> </ul>\n
576 576 s> <ul>\n
577 577 s> \n
578 578 s> </ul>\n
579 579 s> <ul>\n
580 580 s> <li><a href="/redirected/help">help</a></li>\n
581 581 s> </ul>\n
582 582 s> <div class="atom-logo">\n
583 583 s> <a href="/redirected/atom-log" title="subscribe to atom feed">\n
584 584 s> <img class="atom-logo" src="/redirected/static/feed-icon-14x14.png" alt="atom feed" />\n
585 585 s> </a>\n
586 586 s> </div>\n
587 587 s> </div>\n
588 588 s> \n
589 589 s> <div class="main">\n
590 590 s> <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/redirected">redirected</a> </h2>\n
591 591 s> <h3>log</h3>\n
592 592 s> \n
593 593 s> \n
594 594 s> <form class="search" action="/redirected/log">\n
595 595 s> \n
596 596 s> <p><input name="rev" id="search1" type="text" size="30" value="" /></p>\n
597 597 s> <div id="hint">Find changesets by keywords (author, files, the commit message), revision\n
598 598 s> number or hash, or <a href="/redirected/help/revsets">revset expression</a>.</div>\n
599 599 s> </form>\n
600 600 s> \n
601 601 s> <div class="navigate">\n
602 602 s> <a href="/redirected/shortlog/tip?revcount=30">less</a>\n
603 603 s> <a href="/redirected/shortlog/tip?revcount=120">more</a>\n
604 604 s> | rev 0: <a href="/redirected/shortlog/96ee1d7354c4">(0)</a> <a href="/redirected/shortlog/tip">tip</a> \n
605 605 s> </div>\n
606 606 s> \n
607 607 s> <table class="bigtable">\n
608 608 s> <thead>\n
609 609 s> <tr>\n
610 610 s> <th class="age">age</th>\n
611 611 s> <th class="author">author</th>\n
612 612 s> <th class="description">description</th>\n
613 613 s> </tr>\n
614 614 s> </thead>\n
615 615 s> <tbody class="stripes2">\n
616 616 s> <tr>\n
617 617 s> <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>\n
618 618 s> <td class="author">test</td>\n
619 619 s> <td class="description">\n
620 620 s> <a href="/redirected/rev/96ee1d7354c4">initial</a>\n
621 621 s> <span class="phase">draft</span> <span class="branchhead">default</span> <span class="tag">tip</span> \n
622 622 s> </td>\n
623 623 s> </tr>\n
624 624 s> \n
625 625 s> </tbody>\n
626 626 s> </table>\n
627 627 s> \n
628 628 s> <div class="navigate">\n
629 629 s> <a href="/redirected/shortlog/tip?revcount=30">less</a>\n
630 630 s> <a href="/redirected/shortlog/tip?revcount=120">more</a>\n
631 631 s> | rev 0: <a href="/redirected/shortlog/96ee1d7354c4">(0)</a> <a href="/redirected/shortlog/tip">tip</a> \n
632 632 s> </div>\n
633 633 s> \n
634 634 s> <script type="text/javascript">\n
635 635 s> ajaxScrollInit(\n
636 636 s> \'/redirected/shortlog/%next%\',\n
637 637 s> \'\', <!-- NEXTHASH\n
638 638 s> function (htmlText) {
639 639 s> \r\n
640 640 s> 14a\r\n
641 641 s> \n
642 642 s> var m = htmlText.match(/\'(\\w+)\', <!-- NEXTHASH/);\n
643 643 s> return m ? m[1] : null;\n
644 644 s> },\n
645 645 s> \'.bigtable > tbody\',\n
646 646 s> \'<tr class="%class%">\\\n
647 647 s> <td colspan="3" style="text-align: center;">%text%</td>\\\n
648 648 s> </tr>\'\n
649 649 s> );\n
650 650 s> </script>\n
651 651 s> \n
652 652 s> </div>\n
653 653 s> </div>\n
654 654 s> \n
655 655 s> \n
656 656 s> \n
657 657 s> </body>\n
658 658 s> </html>\n
659 659 s> \n
660 660 s> \r\n
661 661 s> 0\r\n
662 662 s> \r\n
663 663
664 664 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT/redirector << EOF
665 665 > command heads
666 666 > EOF
667 667 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
668 668 s> Accept-Encoding: identity\r\n
669 669 s> accept: application/mercurial-0.1\r\n
670 670 s> host: $LOCALIP:$HGPORT\r\n (glob)
671 671 s> user-agent: Mercurial debugwireproto\r\n
672 672 s> \r\n
673 673 s> makefile('rb', None)
674 674 s> HTTP/1.1 301 Redirect\r\n
675 675 s> Server: testing stub value\r\n
676 676 s> Date: $HTTP_DATE$\r\n
677 677 s> Location: http://$LOCALIP:$HGPORT/redirected\r\n (glob)
678 678 s> Content-Type: text/plain\r\n
679 679 s> Content-Length: 10\r\n
680 680 s> \r\n
681 681 s> redirected
682 682 s> GET /redirected HTTP/1.1\r\n
683 683 s> Accept-Encoding: identity\r\n
684 684 s> accept: application/mercurial-0.1\r\n
685 685 s> host: $LOCALIP:$HGPORT\r\n (glob)
686 686 s> user-agent: Mercurial debugwireproto\r\n
687 687 s> \r\n
688 688 s> makefile('rb', None)
689 689 s> HTTP/1.1 200 Script output follows\r\n
690 690 s> Server: testing stub value\r\n
691 691 s> Date: $HTTP_DATE$\r\n
692 692 s> ETag: W/"*"\r\n (glob)
693 693 s> Content-Type: text/html; charset=ascii\r\n
694 694 s> Transfer-Encoding: chunked\r\n
695 695 s> \r\n
696 696 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
697 697 s> 414\r\n
698 698 s> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n
699 699 s> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">\n
700 700 s> <head>\n
701 701 s> <link rel="icon" href="/redirected/static/hgicon.png" type="image/png" />\n
702 702 s> <meta name="robots" content="index, nofollow" />\n
703 703 s> <link rel="stylesheet" href="/redirected/static/style-paper.css" type="text/css" />\n
704 704 s> <script type="text/javascript" src="/redirected/static/mercurial.js"></script>\n
705 705 s> \n
706 706 s> <title>redirected: log</title>\n
707 707 s> <link rel="alternate" type="application/atom+xml"\n
708 708 s> href="/redirected/atom-log" title="Atom feed for redirected" />\n
709 709 s> <link rel="alternate" type="application/rss+xml"\n
710 710 s> href="/redirected/rss-log" title="RSS feed for redirected" />\n
711 711 s> </head>\n
712 712 s> <body>\n
713 713 s> \n
714 714 s> <div class="container">\n
715 715 s> <div class="menu">\n
716 716 s> <div class="logo">\n
717 717 s> <a href="https://mercurial-scm.org/">\n
718 718 s> <img src="/redirected/static/hglogo.png" alt="mercurial" /></a>\n
719 719 s> </div>\n
720 720 s> <ul>\n
721 721 s> <li class="active">log</li>\n
722 722 s> <li><a href="/redirected/graph/tip">graph</a></li>\n
723 723 s> <li><a href="/redirected/tags">tags</a
724 724 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
725 725 s> Accept-Encoding: identity\r\n
726 726 s> accept: application/mercurial-0.1\r\n
727 727 s> host: $LOCALIP:$HGPORT\r\n (glob)
728 728 s> user-agent: Mercurial debugwireproto\r\n
729 729 s> \r\n
730 730 s> makefile('rb', None)
731 731 s> HTTP/1.1 200 Script output follows\r\n
732 732 s> Server: testing stub value\r\n
733 733 s> Date: $HTTP_DATE$\r\n
734 734 s> Content-Type: application/mercurial-0.1\r\n
735 735 s> Content-Length: 467\r\n
736 736 s> \r\n
737 737 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
738 738 s> batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
739 739 sending heads command
740 740 s> GET /redirected?cmd=heads HTTP/1.1\r\n
741 741 s> Accept-Encoding: identity\r\n
742 742 s> vary: X-HgProto-1\r\n
743 743 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
744 744 s> accept: application/mercurial-0.1\r\n
745 745 s> host: $LOCALIP:$HGPORT\r\n (glob)
746 746 s> user-agent: Mercurial debugwireproto\r\n
747 747 s> \r\n
748 748 s> makefile('rb', None)
749 749 s> HTTP/1.1 200 Script output follows\r\n
750 750 s> Server: testing stub value\r\n
751 751 s> Date: $HTTP_DATE$\r\n
752 752 s> Content-Type: application/mercurial-0.1\r\n
753 753 s> Content-Length: 41\r\n
754 754 s> \r\n
755 755 s> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n
756 756 response: [
757 757 b'\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL'
758 758 ]
759 759 (sent 4 HTTP requests and * bytes; received * bytes in responses) (glob)
@@ -1,422 +1,423 b''
1 1 $ . $TESTDIR/wireprotohelpers.sh
2 2 $ cat >> $HGRCPATH << EOF
3 3 > [extensions]
4 4 > blackbox =
5 5 > [blackbox]
6 6 > track = simplecache
7 7 > EOF
8 8 $ hg init server
9 9 $ enablehttpv2 server
10 10 $ cd server
11 11 $ cat >> .hg/hgrc << EOF
12 12 > [extensions]
13 13 > simplecache = $TESTDIR/wireprotosimplecache.py
14 14 > EOF
15 15
16 16 $ echo a0 > a
17 17 $ echo b0 > b
18 18 $ hg -q commit -A -m 'commit 0'
19 19 $ echo a1 > a
20 20 $ hg commit -m 'commit 1'
21 21 $ echo b1 > b
22 22 $ hg commit -m 'commit 2'
23 23 $ echo a2 > a
24 24 $ echo b2 > b
25 25 $ hg commit -m 'commit 3'
26 26
27 27 $ hg log -G -T '{rev}:{node} {desc}'
28 28 @ 3:50590a86f3ff5d1e9a1624a7a6957884565cc8e8 commit 3
29 29 |
30 30 o 2:4d01eda50c6ac5f7e89cbe1880143a32f559c302 commit 2
31 31 |
32 32 o 1:4432d83626e8a98655f062ec1f2a43b07f7fbbb0 commit 1
33 33 |
34 34 o 0:3390ef850073fbc2f0dfff2244342c8e9229013a commit 0
35 35
36 36
37 37 $ hg --debug debugindex -m
38 38 rev linkrev nodeid p1 p2
39 39 0 0 992f4779029a3df8d0666d00bb924f69634e2641 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000
40 40 1 1 a988fb43583e871d1ed5750ee074c6d840bbbfc8 992f4779029a3df8d0666d00bb924f69634e2641 0000000000000000000000000000000000000000
41 41 2 2 a8853dafacfca6fc807055a660d8b835141a3bb4 a988fb43583e871d1ed5750ee074c6d840bbbfc8 0000000000000000000000000000000000000000
42 42 3 3 3fe11dfbb13645782b0addafbe75a87c210ffddc a8853dafacfca6fc807055a660d8b835141a3bb4 0000000000000000000000000000000000000000
43 43
44 44 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
45 45 $ cat hg.pid > $DAEMON_PIDS
46 46
47 47 Performing the same request should result in same result, with 2nd response
48 48 coming from cache.
49 49
50 50 $ sendhttpv2peer << EOF
51 51 > command manifestdata
52 52 > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41']
53 53 > tree eval:b''
54 54 > fields eval:[b'parents']
55 55 > EOF
56 56 creating http peer for wire protocol version 2
57 57 sending manifestdata command
58 58 response: gen[
59 59 {
60 60 b'totalitems': 1
61 61 },
62 62 {
63 63 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
64 64 b'parents': [
65 65 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
66 66 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
67 67 ]
68 68 }
69 69 ]
70 70
71 71 $ sendhttpv2peer << EOF
72 72 > command manifestdata
73 73 > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41']
74 74 > tree eval:b''
75 75 > fields eval:[b'parents']
76 76 > EOF
77 77 creating http peer for wire protocol version 2
78 78 sending manifestdata command
79 79 response: gen[
80 80 {
81 81 b'totalitems': 1
82 82 },
83 83 {
84 84 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
85 85 b'parents': [
86 86 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
87 87 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
88 88 ]
89 89 }
90 90 ]
91 91
92 92 Sending different request doesn't yield cache hit.
93 93
94 94 $ sendhttpv2peer << EOF
95 95 > command manifestdata
96 96 > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41', b'\xa9\x88\xfb\x43\x58\x3e\x87\x1d\x1e\xd5\x75\x0e\xe0\x74\xc6\xd8\x40\xbb\xbf\xc8']
97 97 > tree eval:b''
98 98 > fields eval:[b'parents']
99 99 > EOF
100 100 creating http peer for wire protocol version 2
101 101 sending manifestdata command
102 102 response: gen[
103 103 {
104 104 b'totalitems': 2
105 105 },
106 106 {
107 107 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
108 108 b'parents': [
109 109 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
110 110 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
111 111 ]
112 112 },
113 113 {
114 114 b'node': b'\xa9\x88\xfbCX>\x87\x1d\x1e\xd5u\x0e\xe0t\xc6\xd8@\xbb\xbf\xc8',
115 115 b'parents': [
116 116 b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
117 117 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
118 118 ]
119 119 }
120 120 ]
121 121
122 122 $ cat .hg/blackbox.log
123 123 *> cacher constructed for manifestdata (glob)
124 124 *> cache miss for 64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca (glob)
125 125 *> storing cache entry for 64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca (glob)
126 126 *> cacher constructed for manifestdata (glob)
127 127 *> cache hit for 64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca (glob)
128 128 *> cacher constructed for manifestdata (glob)
129 129 *> cache miss for 1cf89363ec234c6b92d5961281eaa5713e7493f9 (glob)
130 130 *> storing cache entry for 1cf89363ec234c6b92d5961281eaa5713e7493f9 (glob)
131 131
132 132 $ cat error.log
133 133
134 134 $ killdaemons.py
135 135 $ rm .hg/blackbox.log
136 136
137 137 Try with object caching mode
138 138
139 139 $ cat >> .hg/hgrc << EOF
140 140 > [simplecache]
141 141 > cacheobjects = true
142 142 > EOF
143 143
144 144 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
145 145 $ cat hg.pid > $DAEMON_PIDS
146 146
147 147 $ sendhttpv2peer << EOF
148 148 > command manifestdata
149 149 > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41']
150 150 > tree eval:b''
151 151 > fields eval:[b'parents']
152 152 > EOF
153 153 creating http peer for wire protocol version 2
154 154 sending manifestdata command
155 155 response: gen[
156 156 {
157 157 b'totalitems': 1
158 158 },
159 159 {
160 160 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
161 161 b'parents': [
162 162 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
163 163 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
164 164 ]
165 165 }
166 166 ]
167 167
168 168 $ sendhttpv2peer << EOF
169 169 > command manifestdata
170 170 > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41']
171 171 > tree eval:b''
172 172 > fields eval:[b'parents']
173 173 > EOF
174 174 creating http peer for wire protocol version 2
175 175 sending manifestdata command
176 176 response: gen[
177 177 {
178 178 b'totalitems': 1
179 179 },
180 180 {
181 181 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
182 182 b'parents': [
183 183 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
184 184 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
185 185 ]
186 186 }
187 187 ]
188 188
189 189 $ cat .hg/blackbox.log
190 190 *> cacher constructed for manifestdata (glob)
191 191 *> cache miss for 64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca (glob)
192 192 *> storing cache entry for 64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca (glob)
193 193 *> cacher constructed for manifestdata (glob)
194 194 *> cache hit for 64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca (glob)
195 195
196 196 $ cat error.log
197 197
198 198 $ killdaemons.py
199 199 $ rm .hg/blackbox.log
200 200
201 201 A non-cacheable command does not instantiate cacher
202 202
203 203 $ hg serve -p $HGPORT -d --pid-file hg.pid -E error.log
204 204 $ cat hg.pid > $DAEMON_PIDS
205 205 $ sendhttpv2peer << EOF
206 206 > command capabilities
207 207 > EOF
208 208 creating http peer for wire protocol version 2
209 209 sending capabilities command
210 210 response: gen[
211 211 {
212 212 b'commands': {
213 213 b'branchmap': {
214 214 b'args': {},
215 215 b'permissions': [
216 216 b'pull'
217 217 ]
218 218 },
219 219 b'capabilities': {
220 220 b'args': {},
221 221 b'permissions': [
222 222 b'pull'
223 223 ]
224 224 },
225 225 b'changesetdata': {
226 226 b'args': {
227 227 b'fields': {
228 228 b'default': set([]),
229 229 b'required': False,
230 230 b'type': b'set',
231 231 b'validvalues': set([
232 232 b'bookmarks',
233 233 b'parents',
234 234 b'phase',
235 235 b'revision'
236 236 ])
237 237 },
238 238 b'noderange': {
239 239 b'default': None,
240 240 b'required': False,
241 241 b'type': b'list'
242 242 },
243 243 b'nodes': {
244 244 b'default': None,
245 245 b'required': False,
246 246 b'type': b'list'
247 247 },
248 248 b'nodesdepth': {
249 249 b'default': None,
250 250 b'required': False,
251 251 b'type': b'int'
252 252 }
253 253 },
254 254 b'permissions': [
255 255 b'pull'
256 256 ]
257 257 },
258 258 b'filedata': {
259 259 b'args': {
260 260 b'fields': {
261 261 b'default': set([]),
262 262 b'required': False,
263 263 b'type': b'set',
264 264 b'validvalues': set([
265 265 b'parents',
266 266 b'revision'
267 267 ])
268 268 },
269 269 b'haveparents': {
270 270 b'default': False,
271 271 b'required': False,
272 272 b'type': b'bool'
273 273 },
274 274 b'nodes': {
275 275 b'required': True,
276 276 b'type': b'list'
277 277 },
278 278 b'path': {
279 279 b'required': True,
280 280 b'type': b'bytes'
281 281 }
282 282 },
283 283 b'permissions': [
284 284 b'pull'
285 285 ]
286 286 },
287 287 b'heads': {
288 288 b'args': {
289 289 b'publiconly': {
290 290 b'default': False,
291 291 b'required': False,
292 292 b'type': b'bool'
293 293 }
294 294 },
295 295 b'permissions': [
296 296 b'pull'
297 297 ]
298 298 },
299 299 b'known': {
300 300 b'args': {
301 301 b'nodes': {
302 302 b'default': [],
303 303 b'required': False,
304 304 b'type': b'list'
305 305 }
306 306 },
307 307 b'permissions': [
308 308 b'pull'
309 309 ]
310 310 },
311 311 b'listkeys': {
312 312 b'args': {
313 313 b'namespace': {
314 314 b'required': True,
315 315 b'type': b'bytes'
316 316 }
317 317 },
318 318 b'permissions': [
319 319 b'pull'
320 320 ]
321 321 },
322 322 b'lookup': {
323 323 b'args': {
324 324 b'key': {
325 325 b'required': True,
326 326 b'type': b'bytes'
327 327 }
328 328 },
329 329 b'permissions': [
330 330 b'pull'
331 331 ]
332 332 },
333 333 b'manifestdata': {
334 334 b'args': {
335 335 b'fields': {
336 336 b'default': set([]),
337 337 b'required': False,
338 338 b'type': b'set',
339 339 b'validvalues': set([
340 340 b'parents',
341 341 b'revision'
342 342 ])
343 343 },
344 344 b'haveparents': {
345 345 b'default': False,
346 346 b'required': False,
347 347 b'type': b'bool'
348 348 },
349 349 b'nodes': {
350 350 b'required': True,
351 351 b'type': b'list'
352 352 },
353 353 b'tree': {
354 354 b'required': True,
355 355 b'type': b'bytes'
356 356 }
357 357 },
358 358 b'permissions': [
359 359 b'pull'
360 ]
360 ],
361 b'recommendedbatchsize': 100000
361 362 },
362 363 b'pushkey': {
363 364 b'args': {
364 365 b'key': {
365 366 b'required': True,
366 367 b'type': b'bytes'
367 368 },
368 369 b'namespace': {
369 370 b'required': True,
370 371 b'type': b'bytes'
371 372 },
372 373 b'new': {
373 374 b'required': True,
374 375 b'type': b'bytes'
375 376 },
376 377 b'old': {
377 378 b'required': True,
378 379 b'type': b'bytes'
379 380 }
380 381 },
381 382 b'permissions': [
382 383 b'push'
383 384 ]
384 385 }
385 386 },
386 387 b'framingmediatypes': [
387 388 b'application/mercurial-exp-framing-0006'
388 389 ],
389 390 b'pathfilterprefixes': set([
390 391 b'path:',
391 392 b'rootfilesin:'
392 393 ]),
393 394 b'rawrepoformats': [
394 395 b'generaldelta',
395 396 b'revlogv1'
396 397 ]
397 398 }
398 399 ]
399 400
400 401 $ test -f .hg/blackbox.log
401 402 [1]
402 403
403 404 An error is not cached
404 405
405 406 $ sendhttpv2peer << EOF
406 407 > command manifestdata
407 408 > nodes eval:[b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa']
408 409 > tree eval:b''
409 410 > fields eval:[b'parents']
410 411 > EOF
411 412 creating http peer for wire protocol version 2
412 413 sending manifestdata command
413 414 abort: unknown node: \xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa! (esc)
414 415 [255]
415 416
416 417 $ cat .hg/blackbox.log
417 418 *> cacher constructed for manifestdata (glob)
418 419 *> cache miss for 904560928eb95650358f0829d9399b256822eb26 (glob)
419 420 *> cacher exiting due to error (glob)
420 421
421 422 $ killdaemons.py
422 423 $ rm .hg/blackbox.log
@@ -1,668 +1,670 b''
1 1 #require no-chg
2 2
3 3 $ . $TESTDIR/wireprotohelpers.sh
4 4
5 5 $ hg init server
6 6
7 7 zstd isn't present in plain builds. Make tests easier by removing
8 8 zstd from the equation.
9 9
10 10 $ cat >> server/.hg/hgrc << EOF
11 11 > [server]
12 12 > compressionengines = zlib
13 13 > EOF
14 14
15 15 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
16 16 $ cat hg.pid > $DAEMON_PIDS
17 17
18 18 A normal capabilities request is serviced for version 1
19 19
20 20 $ sendhttpraw << EOF
21 21 > httprequest GET ?cmd=capabilities
22 22 > user-agent: test
23 23 > EOF
24 24 using raw connection to peer
25 25 s> GET /?cmd=capabilities HTTP/1.1\r\n
26 26 s> Accept-Encoding: identity\r\n
27 27 s> user-agent: test\r\n
28 28 s> host: $LOCALIP:$HGPORT\r\n (glob)
29 29 s> \r\n
30 30 s> makefile('rb', None)
31 31 s> HTTP/1.1 200 Script output follows\r\n
32 32 s> Server: testing stub value\r\n
33 33 s> Date: $HTTP_DATE$\r\n
34 34 s> Content-Type: application/mercurial-0.1\r\n
35 35 s> Content-Length: *\r\n (glob)
36 36 s> \r\n
37 37 s> batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
38 38
39 39 A proper request without the API server enabled returns the legacy response
40 40
41 41 $ sendhttpraw << EOF
42 42 > httprequest GET ?cmd=capabilities
43 43 > user-agent: test
44 44 > x-hgupgrade-1: foo
45 45 > x-hgproto-1: cbor
46 46 > EOF
47 47 using raw connection to peer
48 48 s> GET /?cmd=capabilities HTTP/1.1\r\n
49 49 s> Accept-Encoding: identity\r\n
50 50 s> user-agent: test\r\n
51 51 s> x-hgproto-1: cbor\r\n
52 52 s> x-hgupgrade-1: foo\r\n
53 53 s> host: $LOCALIP:$HGPORT\r\n (glob)
54 54 s> \r\n
55 55 s> makefile('rb', None)
56 56 s> HTTP/1.1 200 Script output follows\r\n
57 57 s> Server: testing stub value\r\n
58 58 s> Date: $HTTP_DATE$\r\n
59 59 s> Content-Type: application/mercurial-0.1\r\n
60 60 s> Content-Length: *\r\n (glob)
61 61 s> \r\n
62 62 s> batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
63 63
64 64 Restart with just API server enabled. This enables serving the new format.
65 65
66 66 $ killdaemons.py
67 67 $ cat error.log
68 68
69 69 $ cat >> server/.hg/hgrc << EOF
70 70 > [experimental]
71 71 > web.apiserver = true
72 72 > EOF
73 73
74 74 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
75 75 $ cat hg.pid > $DAEMON_PIDS
76 76
77 77 X-HgUpgrade-<N> without CBOR advertisement uses legacy response
78 78
79 79 $ sendhttpraw << EOF
80 80 > httprequest GET ?cmd=capabilities
81 81 > user-agent: test
82 82 > x-hgupgrade-1: foo bar
83 83 > EOF
84 84 using raw connection to peer
85 85 s> GET /?cmd=capabilities HTTP/1.1\r\n
86 86 s> Accept-Encoding: identity\r\n
87 87 s> user-agent: test\r\n
88 88 s> x-hgupgrade-1: foo bar\r\n
89 89 s> host: $LOCALIP:$HGPORT\r\n (glob)
90 90 s> \r\n
91 91 s> makefile('rb', None)
92 92 s> HTTP/1.1 200 Script output follows\r\n
93 93 s> Server: testing stub value\r\n
94 94 s> Date: $HTTP_DATE$\r\n
95 95 s> Content-Type: application/mercurial-0.1\r\n
96 96 s> Content-Length: *\r\n (glob)
97 97 s> \r\n
98 98 s> batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
99 99
100 100 X-HgUpgrade-<N> without known serialization in X-HgProto-<N> uses legacy response
101 101
102 102 $ sendhttpraw << EOF
103 103 > httprequest GET ?cmd=capabilities
104 104 > user-agent: test
105 105 > x-hgupgrade-1: foo bar
106 106 > x-hgproto-1: some value
107 107 > EOF
108 108 using raw connection to peer
109 109 s> GET /?cmd=capabilities HTTP/1.1\r\n
110 110 s> Accept-Encoding: identity\r\n
111 111 s> user-agent: test\r\n
112 112 s> x-hgproto-1: some value\r\n
113 113 s> x-hgupgrade-1: foo bar\r\n
114 114 s> host: $LOCALIP:$HGPORT\r\n (glob)
115 115 s> \r\n
116 116 s> makefile('rb', None)
117 117 s> HTTP/1.1 200 Script output follows\r\n
118 118 s> Server: testing stub value\r\n
119 119 s> Date: $HTTP_DATE$\r\n
120 120 s> Content-Type: application/mercurial-0.1\r\n
121 121 s> Content-Length: *\r\n (glob)
122 122 s> \r\n
123 123 s> batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
124 124
125 125 X-HgUpgrade-<N> + X-HgProto-<N> headers trigger new response format
126 126
127 127 $ sendhttpraw << EOF
128 128 > httprequest GET ?cmd=capabilities
129 129 > user-agent: test
130 130 > x-hgupgrade-1: foo bar
131 131 > x-hgproto-1: cbor
132 132 > EOF
133 133 using raw connection to peer
134 134 s> GET /?cmd=capabilities HTTP/1.1\r\n
135 135 s> Accept-Encoding: identity\r\n
136 136 s> user-agent: test\r\n
137 137 s> x-hgproto-1: cbor\r\n
138 138 s> x-hgupgrade-1: foo bar\r\n
139 139 s> host: $LOCALIP:$HGPORT\r\n (glob)
140 140 s> \r\n
141 141 s> makefile('rb', None)
142 142 s> HTTP/1.1 200 OK\r\n
143 143 s> Server: testing stub value\r\n
144 144 s> Date: $HTTP_DATE$\r\n
145 145 s> Content-Type: application/mercurial-cbor\r\n
146 146 s> Content-Length: *\r\n (glob)
147 147 s> \r\n
148 148 s> \xa3GapibaseDapi/Dapis\xa0Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
149 149 cbor> [
150 150 {
151 151 b'apibase': b'api/',
152 152 b'apis': {},
153 153 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'
154 154 }
155 155 ]
156 156
157 157 Restart server to enable HTTPv2
158 158
159 159 $ killdaemons.py
160 160 $ enablehttpv2 server
161 161 $ hg -R server serve -p $HGPORT -d --pid-file hg.pid -E error.log
162 162 $ cat hg.pid > $DAEMON_PIDS
163 163
164 164 Only requested API services are returned
165 165
166 166 $ sendhttpraw << EOF
167 167 > httprequest GET ?cmd=capabilities
168 168 > user-agent: test
169 169 > x-hgupgrade-1: foo bar
170 170 > x-hgproto-1: cbor
171 171 > EOF
172 172 using raw connection to peer
173 173 s> GET /?cmd=capabilities HTTP/1.1\r\n
174 174 s> Accept-Encoding: identity\r\n
175 175 s> user-agent: test\r\n
176 176 s> x-hgproto-1: cbor\r\n
177 177 s> x-hgupgrade-1: foo bar\r\n
178 178 s> host: $LOCALIP:$HGPORT\r\n (glob)
179 179 s> \r\n
180 180 s> makefile('rb', None)
181 181 s> HTTP/1.1 200 OK\r\n
182 182 s> Server: testing stub value\r\n
183 183 s> Date: $HTTP_DATE$\r\n
184 184 s> Content-Type: application/mercurial-cbor\r\n
185 185 s> Content-Length: *\r\n (glob)
186 186 s> \r\n
187 187 s> \xa3GapibaseDapi/Dapis\xa0Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
188 188 cbor> [
189 189 {
190 190 b'apibase': b'api/',
191 191 b'apis': {},
192 192 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'
193 193 }
194 194 ]
195 195
196 196 Request for HTTPv2 service returns information about it
197 197
198 198 $ sendhttpraw << EOF
199 199 > httprequest GET ?cmd=capabilities
200 200 > user-agent: test
201 201 > x-hgupgrade-1: exp-http-v2-0002 foo bar
202 202 > x-hgproto-1: cbor
203 203 > EOF
204 204 using raw connection to peer
205 205 s> GET /?cmd=capabilities HTTP/1.1\r\n
206 206 s> Accept-Encoding: identity\r\n
207 207 s> user-agent: test\r\n
208 208 s> x-hgproto-1: cbor\r\n
209 209 s> x-hgupgrade-1: exp-http-v2-0002 foo bar\r\n
210 210 s> host: $LOCALIP:$HGPORT\r\n (glob)
211 211 s> \r\n
212 212 s> makefile('rb', None)
213 213 s> HTTP/1.1 200 OK\r\n
214 214 s> Server: testing stub value\r\n
215 215 s> Date: $HTTP_DATE$\r\n
216 216 s> Content-Type: application/mercurial-cbor\r\n
217 217 s> Content-Length: *\r\n (glob)
218 218 s> \r\n
219 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa4Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
219 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa4Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa3Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullTrecommendedbatchsize\x1a\x00\x01\x86\xa0Gpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
220 220 cbor> [
221 221 {
222 222 b'apibase': b'api/',
223 223 b'apis': {
224 224 b'exp-http-v2-0002': {
225 225 b'commands': {
226 226 b'branchmap': {
227 227 b'args': {},
228 228 b'permissions': [
229 229 b'pull'
230 230 ]
231 231 },
232 232 b'capabilities': {
233 233 b'args': {},
234 234 b'permissions': [
235 235 b'pull'
236 236 ]
237 237 },
238 238 b'changesetdata': {
239 239 b'args': {
240 240 b'fields': {
241 241 b'default': set([]),
242 242 b'required': False,
243 243 b'type': b'set',
244 244 b'validvalues': set([
245 245 b'bookmarks',
246 246 b'parents',
247 247 b'phase',
248 248 b'revision'
249 249 ])
250 250 },
251 251 b'noderange': {
252 252 b'default': None,
253 253 b'required': False,
254 254 b'type': b'list'
255 255 },
256 256 b'nodes': {
257 257 b'default': None,
258 258 b'required': False,
259 259 b'type': b'list'
260 260 },
261 261 b'nodesdepth': {
262 262 b'default': None,
263 263 b'required': False,
264 264 b'type': b'int'
265 265 }
266 266 },
267 267 b'permissions': [
268 268 b'pull'
269 269 ]
270 270 },
271 271 b'filedata': {
272 272 b'args': {
273 273 b'fields': {
274 274 b'default': set([]),
275 275 b'required': False,
276 276 b'type': b'set',
277 277 b'validvalues': set([
278 278 b'parents',
279 279 b'revision'
280 280 ])
281 281 },
282 282 b'haveparents': {
283 283 b'default': False,
284 284 b'required': False,
285 285 b'type': b'bool'
286 286 },
287 287 b'nodes': {
288 288 b'required': True,
289 289 b'type': b'list'
290 290 },
291 291 b'path': {
292 292 b'required': True,
293 293 b'type': b'bytes'
294 294 }
295 295 },
296 296 b'permissions': [
297 297 b'pull'
298 298 ]
299 299 },
300 300 b'heads': {
301 301 b'args': {
302 302 b'publiconly': {
303 303 b'default': False,
304 304 b'required': False,
305 305 b'type': b'bool'
306 306 }
307 307 },
308 308 b'permissions': [
309 309 b'pull'
310 310 ]
311 311 },
312 312 b'known': {
313 313 b'args': {
314 314 b'nodes': {
315 315 b'default': [],
316 316 b'required': False,
317 317 b'type': b'list'
318 318 }
319 319 },
320 320 b'permissions': [
321 321 b'pull'
322 322 ]
323 323 },
324 324 b'listkeys': {
325 325 b'args': {
326 326 b'namespace': {
327 327 b'required': True,
328 328 b'type': b'bytes'
329 329 }
330 330 },
331 331 b'permissions': [
332 332 b'pull'
333 333 ]
334 334 },
335 335 b'lookup': {
336 336 b'args': {
337 337 b'key': {
338 338 b'required': True,
339 339 b'type': b'bytes'
340 340 }
341 341 },
342 342 b'permissions': [
343 343 b'pull'
344 344 ]
345 345 },
346 346 b'manifestdata': {
347 347 b'args': {
348 348 b'fields': {
349 349 b'default': set([]),
350 350 b'required': False,
351 351 b'type': b'set',
352 352 b'validvalues': set([
353 353 b'parents',
354 354 b'revision'
355 355 ])
356 356 },
357 357 b'haveparents': {
358 358 b'default': False,
359 359 b'required': False,
360 360 b'type': b'bool'
361 361 },
362 362 b'nodes': {
363 363 b'required': True,
364 364 b'type': b'list'
365 365 },
366 366 b'tree': {
367 367 b'required': True,
368 368 b'type': b'bytes'
369 369 }
370 370 },
371 371 b'permissions': [
372 372 b'pull'
373 ]
373 ],
374 b'recommendedbatchsize': 100000
374 375 },
375 376 b'pushkey': {
376 377 b'args': {
377 378 b'key': {
378 379 b'required': True,
379 380 b'type': b'bytes'
380 381 },
381 382 b'namespace': {
382 383 b'required': True,
383 384 b'type': b'bytes'
384 385 },
385 386 b'new': {
386 387 b'required': True,
387 388 b'type': b'bytes'
388 389 },
389 390 b'old': {
390 391 b'required': True,
391 392 b'type': b'bytes'
392 393 }
393 394 },
394 395 b'permissions': [
395 396 b'push'
396 397 ]
397 398 }
398 399 },
399 400 b'framingmediatypes': [
400 401 b'application/mercurial-exp-framing-0006'
401 402 ],
402 403 b'pathfilterprefixes': set([
403 404 b'path:',
404 405 b'rootfilesin:'
405 406 ]),
406 407 b'rawrepoformats': [
407 408 b'generaldelta',
408 409 b'revlogv1'
409 410 ]
410 411 }
411 412 },
412 413 b'v1capabilities': b'batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash'
413 414 }
414 415 ]
415 416
416 417 capabilities command returns expected info
417 418
418 419 $ sendhttpv2peerhandshake << EOF
419 420 > command capabilities
420 421 > EOF
421 422 creating http peer for wire protocol version 2
422 423 s> GET /?cmd=capabilities HTTP/1.1\r\n
423 424 s> Accept-Encoding: identity\r\n
424 425 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
425 426 s> x-hgproto-1: cbor\r\n
426 427 s> x-hgupgrade-1: exp-http-v2-0002\r\n
427 428 s> accept: application/mercurial-0.1\r\n
428 429 s> host: $LOCALIP:$HGPORT\r\n (glob)
429 430 s> user-agent: Mercurial debugwireproto\r\n
430 431 s> \r\n
431 432 s> makefile('rb', None)
432 433 s> HTTP/1.1 200 OK\r\n
433 434 s> Server: testing stub value\r\n
434 435 s> Date: $HTTP_DATE$\r\n
435 436 s> Content-Type: application/mercurial-cbor\r\n
436 437 s> Content-Length: *\r\n (glob)
437 438 s> \r\n
438 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa4Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
439 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa4Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa3Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullTrecommendedbatchsize\x1a\x00\x01\x86\xa0Gpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
439 440 sending capabilities command
440 441 s> POST /api/exp-http-v2-0002/ro/capabilities HTTP/1.1\r\n
441 442 s> Accept-Encoding: identity\r\n
442 443 s> accept: application/mercurial-exp-framing-0006\r\n
443 444 s> content-type: application/mercurial-exp-framing-0006\r\n
444 445 s> content-length: 63\r\n
445 446 s> host: $LOCALIP:$HGPORT\r\n (glob)
446 447 s> user-agent: Mercurial debugwireproto\r\n
447 448 s> \r\n
448 449 s> \x1c\x00\x00\x01\x00\x01\x01\x82\xa1Pcontentencodings\x81Hidentity\x13\x00\x00\x01\x00\x01\x00\x11\xa1DnameLcapabilities
449 450 s> makefile('rb', None)
450 451 s> HTTP/1.1 200 OK\r\n
451 452 s> Server: testing stub value\r\n
452 453 s> Date: $HTTP_DATE$\r\n
453 454 s> Content-Type: application/mercurial-exp-framing-0006\r\n
454 455 s> Transfer-Encoding: chunked\r\n
455 456 s> \r\n
456 457 s> 11\r\n
457 458 s> \t\x00\x00\x01\x00\x02\x01\x92
458 459 s> Hidentity
459 460 s> \r\n
460 461 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
461 462 s> 13\r\n
462 463 s> \x0b\x00\x00\x01\x00\x02\x041
463 464 s> \xa1FstatusBok
464 465 s> \r\n
465 466 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
466 s> 508\r\n
467 s> \x00\x05\x00\x01\x00\x02\x041
468 s> \xa4Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1
467 s> 522\r\n
468 s> \x1a\x05\x00\x01\x00\x02\x041
469 s> \xa4Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa3Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullTrecommendedbatchsize\x1a\x00\x01\x86\xa0Gpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1
469 470 s> \r\n
470 received frame(size=1280; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
471 received frame(size=1306; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
471 472 s> 8\r\n
472 473 s> \x00\x00\x00\x01\x00\x02\x002
473 474 s> \r\n
474 475 s> 0\r\n
475 476 s> \r\n
476 477 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
477 478 response: gen[
478 479 {
479 480 b'commands': {
480 481 b'branchmap': {
481 482 b'args': {},
482 483 b'permissions': [
483 484 b'pull'
484 485 ]
485 486 },
486 487 b'capabilities': {
487 488 b'args': {},
488 489 b'permissions': [
489 490 b'pull'
490 491 ]
491 492 },
492 493 b'changesetdata': {
493 494 b'args': {
494 495 b'fields': {
495 496 b'default': set([]),
496 497 b'required': False,
497 498 b'type': b'set',
498 499 b'validvalues': set([
499 500 b'bookmarks',
500 501 b'parents',
501 502 b'phase',
502 503 b'revision'
503 504 ])
504 505 },
505 506 b'noderange': {
506 507 b'default': None,
507 508 b'required': False,
508 509 b'type': b'list'
509 510 },
510 511 b'nodes': {
511 512 b'default': None,
512 513 b'required': False,
513 514 b'type': b'list'
514 515 },
515 516 b'nodesdepth': {
516 517 b'default': None,
517 518 b'required': False,
518 519 b'type': b'int'
519 520 }
520 521 },
521 522 b'permissions': [
522 523 b'pull'
523 524 ]
524 525 },
525 526 b'filedata': {
526 527 b'args': {
527 528 b'fields': {
528 529 b'default': set([]),
529 530 b'required': False,
530 531 b'type': b'set',
531 532 b'validvalues': set([
532 533 b'parents',
533 534 b'revision'
534 535 ])
535 536 },
536 537 b'haveparents': {
537 538 b'default': False,
538 539 b'required': False,
539 540 b'type': b'bool'
540 541 },
541 542 b'nodes': {
542 543 b'required': True,
543 544 b'type': b'list'
544 545 },
545 546 b'path': {
546 547 b'required': True,
547 548 b'type': b'bytes'
548 549 }
549 550 },
550 551 b'permissions': [
551 552 b'pull'
552 553 ]
553 554 },
554 555 b'heads': {
555 556 b'args': {
556 557 b'publiconly': {
557 558 b'default': False,
558 559 b'required': False,
559 560 b'type': b'bool'
560 561 }
561 562 },
562 563 b'permissions': [
563 564 b'pull'
564 565 ]
565 566 },
566 567 b'known': {
567 568 b'args': {
568 569 b'nodes': {
569 570 b'default': [],
570 571 b'required': False,
571 572 b'type': b'list'
572 573 }
573 574 },
574 575 b'permissions': [
575 576 b'pull'
576 577 ]
577 578 },
578 579 b'listkeys': {
579 580 b'args': {
580 581 b'namespace': {
581 582 b'required': True,
582 583 b'type': b'bytes'
583 584 }
584 585 },
585 586 b'permissions': [
586 587 b'pull'
587 588 ]
588 589 },
589 590 b'lookup': {
590 591 b'args': {
591 592 b'key': {
592 593 b'required': True,
593 594 b'type': b'bytes'
594 595 }
595 596 },
596 597 b'permissions': [
597 598 b'pull'
598 599 ]
599 600 },
600 601 b'manifestdata': {
601 602 b'args': {
602 603 b'fields': {
603 604 b'default': set([]),
604 605 b'required': False,
605 606 b'type': b'set',
606 607 b'validvalues': set([
607 608 b'parents',
608 609 b'revision'
609 610 ])
610 611 },
611 612 b'haveparents': {
612 613 b'default': False,
613 614 b'required': False,
614 615 b'type': b'bool'
615 616 },
616 617 b'nodes': {
617 618 b'required': True,
618 619 b'type': b'list'
619 620 },
620 621 b'tree': {
621 622 b'required': True,
622 623 b'type': b'bytes'
623 624 }
624 625 },
625 626 b'permissions': [
626 627 b'pull'
627 ]
628 ],
629 b'recommendedbatchsize': 100000
628 630 },
629 631 b'pushkey': {
630 632 b'args': {
631 633 b'key': {
632 634 b'required': True,
633 635 b'type': b'bytes'
634 636 },
635 637 b'namespace': {
636 638 b'required': True,
637 639 b'type': b'bytes'
638 640 },
639 641 b'new': {
640 642 b'required': True,
641 643 b'type': b'bytes'
642 644 },
643 645 b'old': {
644 646 b'required': True,
645 647 b'type': b'bytes'
646 648 }
647 649 },
648 650 b'permissions': [
649 651 b'push'
650 652 ]
651 653 }
652 654 },
653 655 b'framingmediatypes': [
654 656 b'application/mercurial-exp-framing-0006'
655 657 ],
656 658 b'pathfilterprefixes': set([
657 659 b'path:',
658 660 b'rootfilesin:'
659 661 ]),
660 662 b'rawrepoformats': [
661 663 b'generaldelta',
662 664 b'revlogv1'
663 665 ]
664 666 }
665 667 ]
666 668 (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob)
667 669
668 670 $ cat error.log
@@ -1,1310 +1,1314 b''
1 1 $ . $TESTDIR/wireprotohelpers.sh
2 2
3 3 $ cat >> $HGRCPATH << EOF
4 4 > [extensions]
5 5 > blackbox =
6 6 > [blackbox]
7 7 > track = simplecache
8 8 > EOF
9 9
10 10 $ hg init server
11 11 $ enablehttpv2 server
12 12 $ cd server
13 13 $ cat >> .hg/hgrc << EOF
14 14 > [server]
15 15 > compressionengines = zlib
16 16 > [extensions]
17 17 > simplecache = $TESTDIR/wireprotosimplecache.py
18 18 > [simplecache]
19 19 > cacheapi = true
20 20 > EOF
21 21
22 22 $ echo a0 > a
23 23 $ echo b0 > b
24 24 $ hg -q commit -A -m 'commit 0'
25 25 $ echo a1 > a
26 26 $ hg commit -m 'commit 1'
27 27
28 28 $ hg --debug debugindex -m
29 29 rev linkrev nodeid p1 p2
30 30 0 0 992f4779029a3df8d0666d00bb924f69634e2641 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000
31 31 1 1 a988fb43583e871d1ed5750ee074c6d840bbbfc8 992f4779029a3df8d0666d00bb924f69634e2641 0000000000000000000000000000000000000000
32 32
33 33 $ hg --config simplecache.redirectsfile=redirects.py serve -p $HGPORT -d --pid-file hg.pid -E error.log
34 34 $ cat hg.pid > $DAEMON_PIDS
35 35
36 36 $ cat > redirects.py << EOF
37 37 > [
38 38 > {
39 39 > b'name': b'target-a',
40 40 > b'protocol': b'http',
41 41 > b'snirequired': False,
42 42 > b'tlsversions': [b'1.2', b'1.3'],
43 43 > b'uris': [b'http://example.com/'],
44 44 > },
45 45 > ]
46 46 > EOF
47 47
48 48 Redirect targets advertised when configured
49 49
50 50 $ sendhttpv2peerhandshake << EOF
51 51 > command capabilities
52 52 > EOF
53 53 creating http peer for wire protocol version 2
54 54 s> GET /?cmd=capabilities HTTP/1.1\r\n
55 55 s> Accept-Encoding: identity\r\n
56 56 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
57 57 s> x-hgproto-1: cbor\r\n
58 58 s> x-hgupgrade-1: exp-http-v2-0002\r\n
59 59 s> accept: application/mercurial-0.1\r\n
60 60 s> host: $LOCALIP:$HGPORT\r\n (glob)
61 61 s> user-agent: Mercurial debugwireproto\r\n
62 62 s> \r\n
63 63 s> makefile('rb', None)
64 64 s> HTTP/1.1 200 OK\r\n
65 65 s> Server: testing stub value\r\n
66 66 s> Date: $HTTP_DATE$\r\n
67 67 s> Content-Type: application/mercurial-cbor\r\n
68 s> Content-Length: 1930\r\n
68 s> Content-Length: 1956\r\n
69 69 s> \r\n
70 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa5DnameHtarget-aHprotocolDhttpKsnirequired\xf4Ktlsversions\x82C1.2C1.3Duris\x81Shttp://example.com/Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
70 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa3Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullTrecommendedbatchsize\x1a\x00\x01\x86\xa0Gpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa5DnameHtarget-aHprotocolDhttpKsnirequired\xf4Ktlsversions\x82C1.2C1.3Duris\x81Shttp://example.com/Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
71 71 (remote redirect target target-a is compatible)
72 72 sending capabilities command
73 73 s> POST /api/exp-http-v2-0002/ro/capabilities HTTP/1.1\r\n
74 74 s> Accept-Encoding: identity\r\n
75 75 s> accept: application/mercurial-exp-framing-0006\r\n
76 76 s> content-type: application/mercurial-exp-framing-0006\r\n
77 77 s> content-length: 111\r\n
78 78 s> host: $LOCALIP:$HGPORT\r\n (glob)
79 79 s> user-agent: Mercurial debugwireproto\r\n
80 80 s> \r\n
81 81 s> \x1c\x00\x00\x01\x00\x01\x01\x82\xa1Pcontentencodings\x81HidentityC\x00\x00\x01\x00\x01\x00\x11\xa2DnameLcapabilitiesHredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81Htarget-a
82 82 s> makefile('rb', None)
83 83 s> HTTP/1.1 200 OK\r\n
84 84 s> Server: testing stub value\r\n
85 85 s> Date: $HTTP_DATE$\r\n
86 86 s> Content-Type: application/mercurial-exp-framing-0006\r\n
87 87 s> Transfer-Encoding: chunked\r\n
88 88 s> \r\n
89 89 s> 11\r\n
90 90 s> \t\x00\x00\x01\x00\x02\x01\x92
91 91 s> Hidentity
92 92 s> \r\n
93 93 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
94 94 s> 13\r\n
95 95 s> \x0b\x00\x00\x01\x00\x02\x041
96 96 s> \xa1FstatusBok
97 97 s> \r\n
98 98 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
99 s> 588\r\n
100 s> \x80\x05\x00\x01\x00\x02\x041
101 s> \xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa5DnameHtarget-aHprotocolDhttpKsnirequired\xf4Ktlsversions\x82C1.2C1.3Duris\x81Shttp://example.com/
99 s> 5a2\r\n
100 s> \x9a\x05\x00\x01\x00\x02\x041
101 s> \xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa3Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullTrecommendedbatchsize\x1a\x00\x01\x86\xa0Gpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa5DnameHtarget-aHprotocolDhttpKsnirequired\xf4Ktlsversions\x82C1.2C1.3Duris\x81Shttp://example.com/
102 102 s> \r\n
103 received frame(size=1408; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
103 received frame(size=1434; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
104 104 s> 8\r\n
105 105 s> \x00\x00\x00\x01\x00\x02\x002
106 106 s> \r\n
107 107 s> 0\r\n
108 108 s> \r\n
109 109 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
110 110 response: gen[
111 111 {
112 112 b'commands': {
113 113 b'branchmap': {
114 114 b'args': {},
115 115 b'permissions': [
116 116 b'pull'
117 117 ]
118 118 },
119 119 b'capabilities': {
120 120 b'args': {},
121 121 b'permissions': [
122 122 b'pull'
123 123 ]
124 124 },
125 125 b'changesetdata': {
126 126 b'args': {
127 127 b'fields': {
128 128 b'default': set([]),
129 129 b'required': False,
130 130 b'type': b'set',
131 131 b'validvalues': set([
132 132 b'bookmarks',
133 133 b'parents',
134 134 b'phase',
135 135 b'revision'
136 136 ])
137 137 },
138 138 b'noderange': {
139 139 b'default': None,
140 140 b'required': False,
141 141 b'type': b'list'
142 142 },
143 143 b'nodes': {
144 144 b'default': None,
145 145 b'required': False,
146 146 b'type': b'list'
147 147 },
148 148 b'nodesdepth': {
149 149 b'default': None,
150 150 b'required': False,
151 151 b'type': b'int'
152 152 }
153 153 },
154 154 b'permissions': [
155 155 b'pull'
156 156 ]
157 157 },
158 158 b'filedata': {
159 159 b'args': {
160 160 b'fields': {
161 161 b'default': set([]),
162 162 b'required': False,
163 163 b'type': b'set',
164 164 b'validvalues': set([
165 165 b'parents',
166 166 b'revision'
167 167 ])
168 168 },
169 169 b'haveparents': {
170 170 b'default': False,
171 171 b'required': False,
172 172 b'type': b'bool'
173 173 },
174 174 b'nodes': {
175 175 b'required': True,
176 176 b'type': b'list'
177 177 },
178 178 b'path': {
179 179 b'required': True,
180 180 b'type': b'bytes'
181 181 }
182 182 },
183 183 b'permissions': [
184 184 b'pull'
185 185 ]
186 186 },
187 187 b'heads': {
188 188 b'args': {
189 189 b'publiconly': {
190 190 b'default': False,
191 191 b'required': False,
192 192 b'type': b'bool'
193 193 }
194 194 },
195 195 b'permissions': [
196 196 b'pull'
197 197 ]
198 198 },
199 199 b'known': {
200 200 b'args': {
201 201 b'nodes': {
202 202 b'default': [],
203 203 b'required': False,
204 204 b'type': b'list'
205 205 }
206 206 },
207 207 b'permissions': [
208 208 b'pull'
209 209 ]
210 210 },
211 211 b'listkeys': {
212 212 b'args': {
213 213 b'namespace': {
214 214 b'required': True,
215 215 b'type': b'bytes'
216 216 }
217 217 },
218 218 b'permissions': [
219 219 b'pull'
220 220 ]
221 221 },
222 222 b'lookup': {
223 223 b'args': {
224 224 b'key': {
225 225 b'required': True,
226 226 b'type': b'bytes'
227 227 }
228 228 },
229 229 b'permissions': [
230 230 b'pull'
231 231 ]
232 232 },
233 233 b'manifestdata': {
234 234 b'args': {
235 235 b'fields': {
236 236 b'default': set([]),
237 237 b'required': False,
238 238 b'type': b'set',
239 239 b'validvalues': set([
240 240 b'parents',
241 241 b'revision'
242 242 ])
243 243 },
244 244 b'haveparents': {
245 245 b'default': False,
246 246 b'required': False,
247 247 b'type': b'bool'
248 248 },
249 249 b'nodes': {
250 250 b'required': True,
251 251 b'type': b'list'
252 252 },
253 253 b'tree': {
254 254 b'required': True,
255 255 b'type': b'bytes'
256 256 }
257 257 },
258 258 b'permissions': [
259 259 b'pull'
260 ]
260 ],
261 b'recommendedbatchsize': 100000
261 262 },
262 263 b'pushkey': {
263 264 b'args': {
264 265 b'key': {
265 266 b'required': True,
266 267 b'type': b'bytes'
267 268 },
268 269 b'namespace': {
269 270 b'required': True,
270 271 b'type': b'bytes'
271 272 },
272 273 b'new': {
273 274 b'required': True,
274 275 b'type': b'bytes'
275 276 },
276 277 b'old': {
277 278 b'required': True,
278 279 b'type': b'bytes'
279 280 }
280 281 },
281 282 b'permissions': [
282 283 b'push'
283 284 ]
284 285 }
285 286 },
286 287 b'framingmediatypes': [
287 288 b'application/mercurial-exp-framing-0006'
288 289 ],
289 290 b'pathfilterprefixes': set([
290 291 b'path:',
291 292 b'rootfilesin:'
292 293 ]),
293 294 b'rawrepoformats': [
294 295 b'generaldelta',
295 296 b'revlogv1'
296 297 ],
297 298 b'redirect': {
298 299 b'hashes': [
299 300 b'sha256',
300 301 b'sha1'
301 302 ],
302 303 b'targets': [
303 304 {
304 305 b'name': b'target-a',
305 306 b'protocol': b'http',
306 307 b'snirequired': False,
307 308 b'tlsversions': [
308 309 b'1.2',
309 310 b'1.3'
310 311 ],
311 312 b'uris': [
312 313 b'http://example.com/'
313 314 ]
314 315 }
315 316 ]
316 317 }
317 318 }
318 319 ]
319 320 (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob)
320 321
321 322 Unknown protocol is filtered from compatible targets
322 323
323 324 $ cat > redirects.py << EOF
324 325 > [
325 326 > {
326 327 > b'name': b'target-a',
327 328 > b'protocol': b'http',
328 329 > b'uris': [b'http://example.com/'],
329 330 > },
330 331 > {
331 332 > b'name': b'target-b',
332 333 > b'protocol': b'unknown',
333 334 > b'uris': [b'unknown://example.com/'],
334 335 > },
335 336 > ]
336 337 > EOF
337 338
338 339 $ sendhttpv2peerhandshake << EOF
339 340 > command capabilities
340 341 > EOF
341 342 creating http peer for wire protocol version 2
342 343 s> GET /?cmd=capabilities HTTP/1.1\r\n
343 344 s> Accept-Encoding: identity\r\n
344 345 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
345 346 s> x-hgproto-1: cbor\r\n
346 347 s> x-hgupgrade-1: exp-http-v2-0002\r\n
347 348 s> accept: application/mercurial-0.1\r\n
348 349 s> host: $LOCALIP:$HGPORT\r\n (glob)
349 350 s> user-agent: Mercurial debugwireproto\r\n
350 351 s> \r\n
351 352 s> makefile('rb', None)
352 353 s> HTTP/1.1 200 OK\r\n
353 354 s> Server: testing stub value\r\n
354 355 s> Date: $HTTP_DATE$\r\n
355 356 s> Content-Type: application/mercurial-cbor\r\n
356 s> Content-Length: 1957\r\n
357 s> Content-Length: 1983\r\n
357 358 s> \r\n
358 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x82\xa3DnameHtarget-aHprotocolDhttpDuris\x81Shttp://example.com/\xa3DnameHtarget-bHprotocolGunknownDuris\x81Vunknown://example.com/Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
359 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa3Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullTrecommendedbatchsize\x1a\x00\x01\x86\xa0Gpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x82\xa3DnameHtarget-aHprotocolDhttpDuris\x81Shttp://example.com/\xa3DnameHtarget-bHprotocolGunknownDuris\x81Vunknown://example.com/Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
359 360 (remote redirect target target-a is compatible)
360 361 (remote redirect target target-b uses unsupported protocol: unknown)
361 362 sending capabilities command
362 363 s> POST /api/exp-http-v2-0002/ro/capabilities HTTP/1.1\r\n
363 364 s> Accept-Encoding: identity\r\n
364 365 s> accept: application/mercurial-exp-framing-0006\r\n
365 366 s> content-type: application/mercurial-exp-framing-0006\r\n
366 367 s> content-length: 111\r\n
367 368 s> host: $LOCALIP:$HGPORT\r\n (glob)
368 369 s> user-agent: Mercurial debugwireproto\r\n
369 370 s> \r\n
370 371 s> \x1c\x00\x00\x01\x00\x01\x01\x82\xa1Pcontentencodings\x81HidentityC\x00\x00\x01\x00\x01\x00\x11\xa2DnameLcapabilitiesHredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81Htarget-a
371 372 s> makefile('rb', None)
372 373 s> HTTP/1.1 200 OK\r\n
373 374 s> Server: testing stub value\r\n
374 375 s> Date: $HTTP_DATE$\r\n
375 376 s> Content-Type: application/mercurial-exp-framing-0006\r\n
376 377 s> Transfer-Encoding: chunked\r\n
377 378 s> \r\n
378 379 s> 11\r\n
379 380 s> \t\x00\x00\x01\x00\x02\x01\x92
380 381 s> Hidentity
381 382 s> \r\n
382 383 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
383 384 s> 13\r\n
384 385 s> \x0b\x00\x00\x01\x00\x02\x041
385 386 s> \xa1FstatusBok
386 387 s> \r\n
387 388 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
388 s> 5a3\r\n
389 s> \x9b\x05\x00\x01\x00\x02\x041
390 s> \xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x82\xa3DnameHtarget-aHprotocolDhttpDuris\x81Shttp://example.com/\xa3DnameHtarget-bHprotocolGunknownDuris\x81Vunknown://example.com/
389 s> 5bd\r\n
390 s> \xb5\x05\x00\x01\x00\x02\x041
391 s> \xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa3Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullTrecommendedbatchsize\x1a\x00\x01\x86\xa0Gpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x82\xa3DnameHtarget-aHprotocolDhttpDuris\x81Shttp://example.com/\xa3DnameHtarget-bHprotocolGunknownDuris\x81Vunknown://example.com/
391 392 s> \r\n
392 received frame(size=1435; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
393 received frame(size=1461; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
393 394 s> 8\r\n
394 395 s> \x00\x00\x00\x01\x00\x02\x002
395 396 s> \r\n
396 397 s> 0\r\n
397 398 s> \r\n
398 399 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
399 400 response: gen[
400 401 {
401 402 b'commands': {
402 403 b'branchmap': {
403 404 b'args': {},
404 405 b'permissions': [
405 406 b'pull'
406 407 ]
407 408 },
408 409 b'capabilities': {
409 410 b'args': {},
410 411 b'permissions': [
411 412 b'pull'
412 413 ]
413 414 },
414 415 b'changesetdata': {
415 416 b'args': {
416 417 b'fields': {
417 418 b'default': set([]),
418 419 b'required': False,
419 420 b'type': b'set',
420 421 b'validvalues': set([
421 422 b'bookmarks',
422 423 b'parents',
423 424 b'phase',
424 425 b'revision'
425 426 ])
426 427 },
427 428 b'noderange': {
428 429 b'default': None,
429 430 b'required': False,
430 431 b'type': b'list'
431 432 },
432 433 b'nodes': {
433 434 b'default': None,
434 435 b'required': False,
435 436 b'type': b'list'
436 437 },
437 438 b'nodesdepth': {
438 439 b'default': None,
439 440 b'required': False,
440 441 b'type': b'int'
441 442 }
442 443 },
443 444 b'permissions': [
444 445 b'pull'
445 446 ]
446 447 },
447 448 b'filedata': {
448 449 b'args': {
449 450 b'fields': {
450 451 b'default': set([]),
451 452 b'required': False,
452 453 b'type': b'set',
453 454 b'validvalues': set([
454 455 b'parents',
455 456 b'revision'
456 457 ])
457 458 },
458 459 b'haveparents': {
459 460 b'default': False,
460 461 b'required': False,
461 462 b'type': b'bool'
462 463 },
463 464 b'nodes': {
464 465 b'required': True,
465 466 b'type': b'list'
466 467 },
467 468 b'path': {
468 469 b'required': True,
469 470 b'type': b'bytes'
470 471 }
471 472 },
472 473 b'permissions': [
473 474 b'pull'
474 475 ]
475 476 },
476 477 b'heads': {
477 478 b'args': {
478 479 b'publiconly': {
479 480 b'default': False,
480 481 b'required': False,
481 482 b'type': b'bool'
482 483 }
483 484 },
484 485 b'permissions': [
485 486 b'pull'
486 487 ]
487 488 },
488 489 b'known': {
489 490 b'args': {
490 491 b'nodes': {
491 492 b'default': [],
492 493 b'required': False,
493 494 b'type': b'list'
494 495 }
495 496 },
496 497 b'permissions': [
497 498 b'pull'
498 499 ]
499 500 },
500 501 b'listkeys': {
501 502 b'args': {
502 503 b'namespace': {
503 504 b'required': True,
504 505 b'type': b'bytes'
505 506 }
506 507 },
507 508 b'permissions': [
508 509 b'pull'
509 510 ]
510 511 },
511 512 b'lookup': {
512 513 b'args': {
513 514 b'key': {
514 515 b'required': True,
515 516 b'type': b'bytes'
516 517 }
517 518 },
518 519 b'permissions': [
519 520 b'pull'
520 521 ]
521 522 },
522 523 b'manifestdata': {
523 524 b'args': {
524 525 b'fields': {
525 526 b'default': set([]),
526 527 b'required': False,
527 528 b'type': b'set',
528 529 b'validvalues': set([
529 530 b'parents',
530 531 b'revision'
531 532 ])
532 533 },
533 534 b'haveparents': {
534 535 b'default': False,
535 536 b'required': False,
536 537 b'type': b'bool'
537 538 },
538 539 b'nodes': {
539 540 b'required': True,
540 541 b'type': b'list'
541 542 },
542 543 b'tree': {
543 544 b'required': True,
544 545 b'type': b'bytes'
545 546 }
546 547 },
547 548 b'permissions': [
548 549 b'pull'
549 ]
550 ],
551 b'recommendedbatchsize': 100000
550 552 },
551 553 b'pushkey': {
552 554 b'args': {
553 555 b'key': {
554 556 b'required': True,
555 557 b'type': b'bytes'
556 558 },
557 559 b'namespace': {
558 560 b'required': True,
559 561 b'type': b'bytes'
560 562 },
561 563 b'new': {
562 564 b'required': True,
563 565 b'type': b'bytes'
564 566 },
565 567 b'old': {
566 568 b'required': True,
567 569 b'type': b'bytes'
568 570 }
569 571 },
570 572 b'permissions': [
571 573 b'push'
572 574 ]
573 575 }
574 576 },
575 577 b'framingmediatypes': [
576 578 b'application/mercurial-exp-framing-0006'
577 579 ],
578 580 b'pathfilterprefixes': set([
579 581 b'path:',
580 582 b'rootfilesin:'
581 583 ]),
582 584 b'rawrepoformats': [
583 585 b'generaldelta',
584 586 b'revlogv1'
585 587 ],
586 588 b'redirect': {
587 589 b'hashes': [
588 590 b'sha256',
589 591 b'sha1'
590 592 ],
591 593 b'targets': [
592 594 {
593 595 b'name': b'target-a',
594 596 b'protocol': b'http',
595 597 b'uris': [
596 598 b'http://example.com/'
597 599 ]
598 600 },
599 601 {
600 602 b'name': b'target-b',
601 603 b'protocol': b'unknown',
602 604 b'uris': [
603 605 b'unknown://example.com/'
604 606 ]
605 607 }
606 608 ]
607 609 }
608 610 }
609 611 ]
610 612 (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob)
611 613
612 614 Missing SNI support filters targets that require SNI
613 615
614 616 $ cat > nosni.py << EOF
615 617 > from mercurial import sslutil
616 618 > sslutil.hassni = False
617 619 > EOF
618 620 $ cat >> $HGRCPATH << EOF
619 621 > [extensions]
620 622 > nosni=`pwd`/nosni.py
621 623 > EOF
622 624
623 625 $ cat > redirects.py << EOF
624 626 > [
625 627 > {
626 628 > b'name': b'target-bad-tls',
627 629 > b'protocol': b'https',
628 630 > b'uris': [b'https://example.com/'],
629 631 > b'snirequired': True,
630 632 > },
631 633 > ]
632 634 > EOF
633 635
634 636 $ sendhttpv2peerhandshake << EOF
635 637 > command capabilities
636 638 > EOF
637 639 creating http peer for wire protocol version 2
638 640 s> GET /?cmd=capabilities HTTP/1.1\r\n
639 641 s> Accept-Encoding: identity\r\n
640 642 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
641 643 s> x-hgproto-1: cbor\r\n
642 644 s> x-hgupgrade-1: exp-http-v2-0002\r\n
643 645 s> accept: application/mercurial-0.1\r\n
644 646 s> host: $LOCALIP:$HGPORT\r\n (glob)
645 647 s> user-agent: Mercurial debugwireproto\r\n
646 648 s> \r\n
647 649 s> makefile('rb', None)
648 650 s> HTTP/1.1 200 OK\r\n
649 651 s> Server: testing stub value\r\n
650 652 s> Date: $HTTP_DATE$\r\n
651 653 s> Content-Type: application/mercurial-cbor\r\n
652 s> Content-Length: 1917\r\n
654 s> Content-Length: 1943\r\n
653 655 s> \r\n
654 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa4DnameNtarget-bad-tlsHprotocolEhttpsKsnirequired\xf5Duris\x81Thttps://example.com/Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
656 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa3Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullTrecommendedbatchsize\x1a\x00\x01\x86\xa0Gpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa4DnameNtarget-bad-tlsHprotocolEhttpsKsnirequired\xf5Duris\x81Thttps://example.com/Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
655 657 (redirect target target-bad-tls requires SNI, which is unsupported)
656 658 sending capabilities command
657 659 s> POST /api/exp-http-v2-0002/ro/capabilities HTTP/1.1\r\n
658 660 s> Accept-Encoding: identity\r\n
659 661 s> accept: application/mercurial-exp-framing-0006\r\n
660 662 s> content-type: application/mercurial-exp-framing-0006\r\n
661 663 s> content-length: 102\r\n
662 664 s> host: $LOCALIP:$HGPORT\r\n (glob)
663 665 s> user-agent: Mercurial debugwireproto\r\n
664 666 s> \r\n
665 667 s> \x1c\x00\x00\x01\x00\x01\x01\x82\xa1Pcontentencodings\x81Hidentity:\x00\x00\x01\x00\x01\x00\x11\xa2DnameLcapabilitiesHredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x80
666 668 s> makefile('rb', None)
667 669 s> HTTP/1.1 200 OK\r\n
668 670 s> Server: testing stub value\r\n
669 671 s> Date: $HTTP_DATE$\r\n
670 672 s> Content-Type: application/mercurial-exp-framing-0006\r\n
671 673 s> Transfer-Encoding: chunked\r\n
672 674 s> \r\n
673 675 s> 11\r\n
674 676 s> \t\x00\x00\x01\x00\x02\x01\x92
675 677 s> Hidentity
676 678 s> \r\n
677 679 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
678 680 s> 13\r\n
679 681 s> \x0b\x00\x00\x01\x00\x02\x041
680 682 s> \xa1FstatusBok
681 683 s> \r\n
682 684 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
683 s> 57b\r\n
684 s> s\x05\x00\x01\x00\x02\x041
685 s> \xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa4DnameNtarget-bad-tlsHprotocolEhttpsKsnirequired\xf5Duris\x81Thttps://example.com/
685 s> 595\r\n
686 s> \x8d\x05\x00\x01\x00\x02\x041
687 s> \xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa3Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullTrecommendedbatchsize\x1a\x00\x01\x86\xa0Gpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa4DnameNtarget-bad-tlsHprotocolEhttpsKsnirequired\xf5Duris\x81Thttps://example.com/
686 688 s> \r\n
687 received frame(size=1395; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
689 received frame(size=1421; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
688 690 s> 8\r\n
689 691 s> \x00\x00\x00\x01\x00\x02\x002
690 692 s> \r\n
691 693 s> 0\r\n
692 694 s> \r\n
693 695 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
694 696 response: gen[
695 697 {
696 698 b'commands': {
697 699 b'branchmap': {
698 700 b'args': {},
699 701 b'permissions': [
700 702 b'pull'
701 703 ]
702 704 },
703 705 b'capabilities': {
704 706 b'args': {},
705 707 b'permissions': [
706 708 b'pull'
707 709 ]
708 710 },
709 711 b'changesetdata': {
710 712 b'args': {
711 713 b'fields': {
712 714 b'default': set([]),
713 715 b'required': False,
714 716 b'type': b'set',
715 717 b'validvalues': set([
716 718 b'bookmarks',
717 719 b'parents',
718 720 b'phase',
719 721 b'revision'
720 722 ])
721 723 },
722 724 b'noderange': {
723 725 b'default': None,
724 726 b'required': False,
725 727 b'type': b'list'
726 728 },
727 729 b'nodes': {
728 730 b'default': None,
729 731 b'required': False,
730 732 b'type': b'list'
731 733 },
732 734 b'nodesdepth': {
733 735 b'default': None,
734 736 b'required': False,
735 737 b'type': b'int'
736 738 }
737 739 },
738 740 b'permissions': [
739 741 b'pull'
740 742 ]
741 743 },
742 744 b'filedata': {
743 745 b'args': {
744 746 b'fields': {
745 747 b'default': set([]),
746 748 b'required': False,
747 749 b'type': b'set',
748 750 b'validvalues': set([
749 751 b'parents',
750 752 b'revision'
751 753 ])
752 754 },
753 755 b'haveparents': {
754 756 b'default': False,
755 757 b'required': False,
756 758 b'type': b'bool'
757 759 },
758 760 b'nodes': {
759 761 b'required': True,
760 762 b'type': b'list'
761 763 },
762 764 b'path': {
763 765 b'required': True,
764 766 b'type': b'bytes'
765 767 }
766 768 },
767 769 b'permissions': [
768 770 b'pull'
769 771 ]
770 772 },
771 773 b'heads': {
772 774 b'args': {
773 775 b'publiconly': {
774 776 b'default': False,
775 777 b'required': False,
776 778 b'type': b'bool'
777 779 }
778 780 },
779 781 b'permissions': [
780 782 b'pull'
781 783 ]
782 784 },
783 785 b'known': {
784 786 b'args': {
785 787 b'nodes': {
786 788 b'default': [],
787 789 b'required': False,
788 790 b'type': b'list'
789 791 }
790 792 },
791 793 b'permissions': [
792 794 b'pull'
793 795 ]
794 796 },
795 797 b'listkeys': {
796 798 b'args': {
797 799 b'namespace': {
798 800 b'required': True,
799 801 b'type': b'bytes'
800 802 }
801 803 },
802 804 b'permissions': [
803 805 b'pull'
804 806 ]
805 807 },
806 808 b'lookup': {
807 809 b'args': {
808 810 b'key': {
809 811 b'required': True,
810 812 b'type': b'bytes'
811 813 }
812 814 },
813 815 b'permissions': [
814 816 b'pull'
815 817 ]
816 818 },
817 819 b'manifestdata': {
818 820 b'args': {
819 821 b'fields': {
820 822 b'default': set([]),
821 823 b'required': False,
822 824 b'type': b'set',
823 825 b'validvalues': set([
824 826 b'parents',
825 827 b'revision'
826 828 ])
827 829 },
828 830 b'haveparents': {
829 831 b'default': False,
830 832 b'required': False,
831 833 b'type': b'bool'
832 834 },
833 835 b'nodes': {
834 836 b'required': True,
835 837 b'type': b'list'
836 838 },
837 839 b'tree': {
838 840 b'required': True,
839 841 b'type': b'bytes'
840 842 }
841 843 },
842 844 b'permissions': [
843 845 b'pull'
844 ]
846 ],
847 b'recommendedbatchsize': 100000
845 848 },
846 849 b'pushkey': {
847 850 b'args': {
848 851 b'key': {
849 852 b'required': True,
850 853 b'type': b'bytes'
851 854 },
852 855 b'namespace': {
853 856 b'required': True,
854 857 b'type': b'bytes'
855 858 },
856 859 b'new': {
857 860 b'required': True,
858 861 b'type': b'bytes'
859 862 },
860 863 b'old': {
861 864 b'required': True,
862 865 b'type': b'bytes'
863 866 }
864 867 },
865 868 b'permissions': [
866 869 b'push'
867 870 ]
868 871 }
869 872 },
870 873 b'framingmediatypes': [
871 874 b'application/mercurial-exp-framing-0006'
872 875 ],
873 876 b'pathfilterprefixes': set([
874 877 b'path:',
875 878 b'rootfilesin:'
876 879 ]),
877 880 b'rawrepoformats': [
878 881 b'generaldelta',
879 882 b'revlogv1'
880 883 ],
881 884 b'redirect': {
882 885 b'hashes': [
883 886 b'sha256',
884 887 b'sha1'
885 888 ],
886 889 b'targets': [
887 890 {
888 891 b'name': b'target-bad-tls',
889 892 b'protocol': b'https',
890 893 b'snirequired': True,
891 894 b'uris': [
892 895 b'https://example.com/'
893 896 ]
894 897 }
895 898 ]
896 899 }
897 900 }
898 901 ]
899 902 (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob)
900 903
901 904 $ cat >> $HGRCPATH << EOF
902 905 > [extensions]
903 906 > nosni=!
904 907 > EOF
905 908
906 909 Unknown tls value is filtered from compatible targets
907 910
908 911 $ cat > redirects.py << EOF
909 912 > [
910 913 > {
911 914 > b'name': b'target-bad-tls',
912 915 > b'protocol': b'https',
913 916 > b'uris': [b'https://example.com/'],
914 917 > b'tlsversions': [b'42', b'39'],
915 918 > },
916 919 > ]
917 920 > EOF
918 921
919 922 $ sendhttpv2peerhandshake << EOF
920 923 > command capabilities
921 924 > EOF
922 925 creating http peer for wire protocol version 2
923 926 s> GET /?cmd=capabilities HTTP/1.1\r\n
924 927 s> Accept-Encoding: identity\r\n
925 928 s> vary: X-HgProto-1,X-HgUpgrade-1\r\n
926 929 s> x-hgproto-1: cbor\r\n
927 930 s> x-hgupgrade-1: exp-http-v2-0002\r\n
928 931 s> accept: application/mercurial-0.1\r\n
929 932 s> host: $LOCALIP:$HGPORT\r\n (glob)
930 933 s> user-agent: Mercurial debugwireproto\r\n
931 934 s> \r\n
932 935 s> makefile('rb', None)
933 936 s> HTTP/1.1 200 OK\r\n
934 937 s> Server: testing stub value\r\n
935 938 s> Date: $HTTP_DATE$\r\n
936 939 s> Content-Type: application/mercurial-cbor\r\n
937 s> Content-Length: 1923\r\n
940 s> Content-Length: 1949\r\n
938 941 s> \r\n
939 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa4DnameNtarget-bad-tlsHprotocolEhttpsKtlsversions\x82B42B39Duris\x81Thttps://example.com/Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
942 s> \xa3GapibaseDapi/Dapis\xa1Pexp-http-v2-0002\xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa3Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullTrecommendedbatchsize\x1a\x00\x01\x86\xa0Gpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa4DnameNtarget-bad-tlsHprotocolEhttpsKtlsversions\x82B42B39Duris\x81Thttps://example.com/Nv1capabilitiesY\x01\xd3batch branchmap $USUAL_BUNDLE2_CAPS$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
940 943 (remote redirect target target-bad-tls requires unsupported TLS versions: 39, 42)
941 944 sending capabilities command
942 945 s> POST /api/exp-http-v2-0002/ro/capabilities HTTP/1.1\r\n
943 946 s> Accept-Encoding: identity\r\n
944 947 s> accept: application/mercurial-exp-framing-0006\r\n
945 948 s> content-type: application/mercurial-exp-framing-0006\r\n
946 949 s> content-length: 102\r\n
947 950 s> host: $LOCALIP:$HGPORT\r\n (glob)
948 951 s> user-agent: Mercurial debugwireproto\r\n
949 952 s> \r\n
950 953 s> \x1c\x00\x00\x01\x00\x01\x01\x82\xa1Pcontentencodings\x81Hidentity:\x00\x00\x01\x00\x01\x00\x11\xa2DnameLcapabilitiesHredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x80
951 954 s> makefile('rb', None)
952 955 s> HTTP/1.1 200 OK\r\n
953 956 s> Server: testing stub value\r\n
954 957 s> Date: $HTTP_DATE$\r\n
955 958 s> Content-Type: application/mercurial-exp-framing-0006\r\n
956 959 s> Transfer-Encoding: chunked\r\n
957 960 s> \r\n
958 961 s> 11\r\n
959 962 s> \t\x00\x00\x01\x00\x02\x01\x92
960 963 s> Hidentity
961 964 s> \r\n
962 965 received frame(size=9; request=1; stream=2; streamflags=stream-begin; type=stream-settings; flags=eos)
963 966 s> 13\r\n
964 967 s> \x0b\x00\x00\x01\x00\x02\x041
965 968 s> \xa1FstatusBok
966 969 s> \r\n
967 970 received frame(size=11; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
968 s> 581\r\n
969 s> y\x05\x00\x01\x00\x02\x041
970 s> \xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullGpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa4DnameNtarget-bad-tlsHprotocolEhttpsKtlsversions\x82B42B39Duris\x81Thttps://example.com/
971 s> 59b\r\n
972 s> \x93\x05\x00\x01\x00\x02\x041
973 s> \xa5Hcommands\xaaIbranchmap\xa2Dargs\xa0Kpermissions\x81DpullLcapabilities\xa2Dargs\xa0Kpermissions\x81DpullMchangesetdata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x84IbookmarksGparentsEphaseHrevisionInoderange\xa3Gdefault\xf6Hrequired\xf4DtypeDlistEnodes\xa3Gdefault\xf6Hrequired\xf4DtypeDlistJnodesdepth\xa3Gdefault\xf6Hrequired\xf4DtypeCintKpermissions\x81DpullHfiledata\xa2Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDpath\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullEheads\xa2Dargs\xa1Jpubliconly\xa3Gdefault\xf4Hrequired\xf4DtypeDboolKpermissions\x81DpullEknown\xa2Dargs\xa1Enodes\xa3Gdefault\x80Hrequired\xf4DtypeDlistKpermissions\x81DpullHlistkeys\xa2Dargs\xa1Inamespace\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullFlookup\xa2Dargs\xa1Ckey\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullLmanifestdata\xa3Dargs\xa4Ffields\xa4Gdefault\xd9\x01\x02\x80Hrequired\xf4DtypeCsetKvalidvalues\xd9\x01\x02\x82GparentsHrevisionKhaveparents\xa3Gdefault\xf4Hrequired\xf4DtypeDboolEnodes\xa2Hrequired\xf5DtypeDlistDtree\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpullTrecommendedbatchsize\x1a\x00\x01\x86\xa0Gpushkey\xa2Dargs\xa4Ckey\xa2Hrequired\xf5DtypeEbytesInamespace\xa2Hrequired\xf5DtypeEbytesCnew\xa2Hrequired\xf5DtypeEbytesCold\xa2Hrequired\xf5DtypeEbytesKpermissions\x81DpushQframingmediatypes\x81X&application/mercurial-exp-framing-0006Rpathfilterprefixes\xd9\x01\x02\x82Epath:Lrootfilesin:Nrawrepoformats\x82LgeneraldeltaHrevlogv1Hredirect\xa2Fhashes\x82Fsha256Dsha1Gtargets\x81\xa4DnameNtarget-bad-tlsHprotocolEhttpsKtlsversions\x82B42B39Duris\x81Thttps://example.com/
971 974 s> \r\n
972 received frame(size=1401; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
975 received frame(size=1427; request=1; stream=2; streamflags=encoded; type=command-response; flags=continuation)
973 976 s> 8\r\n
974 977 s> \x00\x00\x00\x01\x00\x02\x002
975 978 s> \r\n
976 979 s> 0\r\n
977 980 s> \r\n
978 981 received frame(size=0; request=1; stream=2; streamflags=; type=command-response; flags=eos)
979 982 response: gen[
980 983 {
981 984 b'commands': {
982 985 b'branchmap': {
983 986 b'args': {},
984 987 b'permissions': [
985 988 b'pull'
986 989 ]
987 990 },
988 991 b'capabilities': {
989 992 b'args': {},
990 993 b'permissions': [
991 994 b'pull'
992 995 ]
993 996 },
994 997 b'changesetdata': {
995 998 b'args': {
996 999 b'fields': {
997 1000 b'default': set([]),
998 1001 b'required': False,
999 1002 b'type': b'set',
1000 1003 b'validvalues': set([
1001 1004 b'bookmarks',
1002 1005 b'parents',
1003 1006 b'phase',
1004 1007 b'revision'
1005 1008 ])
1006 1009 },
1007 1010 b'noderange': {
1008 1011 b'default': None,
1009 1012 b'required': False,
1010 1013 b'type': b'list'
1011 1014 },
1012 1015 b'nodes': {
1013 1016 b'default': None,
1014 1017 b'required': False,
1015 1018 b'type': b'list'
1016 1019 },
1017 1020 b'nodesdepth': {
1018 1021 b'default': None,
1019 1022 b'required': False,
1020 1023 b'type': b'int'
1021 1024 }
1022 1025 },
1023 1026 b'permissions': [
1024 1027 b'pull'
1025 1028 ]
1026 1029 },
1027 1030 b'filedata': {
1028 1031 b'args': {
1029 1032 b'fields': {
1030 1033 b'default': set([]),
1031 1034 b'required': False,
1032 1035 b'type': b'set',
1033 1036 b'validvalues': set([
1034 1037 b'parents',
1035 1038 b'revision'
1036 1039 ])
1037 1040 },
1038 1041 b'haveparents': {
1039 1042 b'default': False,
1040 1043 b'required': False,
1041 1044 b'type': b'bool'
1042 1045 },
1043 1046 b'nodes': {
1044 1047 b'required': True,
1045 1048 b'type': b'list'
1046 1049 },
1047 1050 b'path': {
1048 1051 b'required': True,
1049 1052 b'type': b'bytes'
1050 1053 }
1051 1054 },
1052 1055 b'permissions': [
1053 1056 b'pull'
1054 1057 ]
1055 1058 },
1056 1059 b'heads': {
1057 1060 b'args': {
1058 1061 b'publiconly': {
1059 1062 b'default': False,
1060 1063 b'required': False,
1061 1064 b'type': b'bool'
1062 1065 }
1063 1066 },
1064 1067 b'permissions': [
1065 1068 b'pull'
1066 1069 ]
1067 1070 },
1068 1071 b'known': {
1069 1072 b'args': {
1070 1073 b'nodes': {
1071 1074 b'default': [],
1072 1075 b'required': False,
1073 1076 b'type': b'list'
1074 1077 }
1075 1078 },
1076 1079 b'permissions': [
1077 1080 b'pull'
1078 1081 ]
1079 1082 },
1080 1083 b'listkeys': {
1081 1084 b'args': {
1082 1085 b'namespace': {
1083 1086 b'required': True,
1084 1087 b'type': b'bytes'
1085 1088 }
1086 1089 },
1087 1090 b'permissions': [
1088 1091 b'pull'
1089 1092 ]
1090 1093 },
1091 1094 b'lookup': {
1092 1095 b'args': {
1093 1096 b'key': {
1094 1097 b'required': True,
1095 1098 b'type': b'bytes'
1096 1099 }
1097 1100 },
1098 1101 b'permissions': [
1099 1102 b'pull'
1100 1103 ]
1101 1104 },
1102 1105 b'manifestdata': {
1103 1106 b'args': {
1104 1107 b'fields': {
1105 1108 b'default': set([]),
1106 1109 b'required': False,
1107 1110 b'type': b'set',
1108 1111 b'validvalues': set([
1109 1112 b'parents',
1110 1113 b'revision'
1111 1114 ])
1112 1115 },
1113 1116 b'haveparents': {
1114 1117 b'default': False,
1115 1118 b'required': False,
1116 1119 b'type': b'bool'
1117 1120 },
1118 1121 b'nodes': {
1119 1122 b'required': True,
1120 1123 b'type': b'list'
1121 1124 },
1122 1125 b'tree': {
1123 1126 b'required': True,
1124 1127 b'type': b'bytes'
1125 1128 }
1126 1129 },
1127 1130 b'permissions': [
1128 1131 b'pull'
1129 ]
1132 ],
1133 b'recommendedbatchsize': 100000
1130 1134 },
1131 1135 b'pushkey': {
1132 1136 b'args': {
1133 1137 b'key': {
1134 1138 b'required': True,
1135 1139 b'type': b'bytes'
1136 1140 },
1137 1141 b'namespace': {
1138 1142 b'required': True,
1139 1143 b'type': b'bytes'
1140 1144 },
1141 1145 b'new': {
1142 1146 b'required': True,
1143 1147 b'type': b'bytes'
1144 1148 },
1145 1149 b'old': {
1146 1150 b'required': True,
1147 1151 b'type': b'bytes'
1148 1152 }
1149 1153 },
1150 1154 b'permissions': [
1151 1155 b'push'
1152 1156 ]
1153 1157 }
1154 1158 },
1155 1159 b'framingmediatypes': [
1156 1160 b'application/mercurial-exp-framing-0006'
1157 1161 ],
1158 1162 b'pathfilterprefixes': set([
1159 1163 b'path:',
1160 1164 b'rootfilesin:'
1161 1165 ]),
1162 1166 b'rawrepoformats': [
1163 1167 b'generaldelta',
1164 1168 b'revlogv1'
1165 1169 ],
1166 1170 b'redirect': {
1167 1171 b'hashes': [
1168 1172 b'sha256',
1169 1173 b'sha1'
1170 1174 ],
1171 1175 b'targets': [
1172 1176 {
1173 1177 b'name': b'target-bad-tls',
1174 1178 b'protocol': b'https',
1175 1179 b'tlsversions': [
1176 1180 b'42',
1177 1181 b'39'
1178 1182 ],
1179 1183 b'uris': [
1180 1184 b'https://example.com/'
1181 1185 ]
1182 1186 }
1183 1187 ]
1184 1188 }
1185 1189 }
1186 1190 ]
1187 1191 (sent 2 HTTP requests and * bytes; received * bytes in responses) (glob)
1188 1192
1189 1193 Set up the server to issue content redirects to its built-in API server.
1190 1194
1191 1195 $ cat > redirects.py << EOF
1192 1196 > [
1193 1197 > {
1194 1198 > b'name': b'local',
1195 1199 > b'protocol': b'http',
1196 1200 > b'uris': [b'http://example.com/'],
1197 1201 > },
1198 1202 > ]
1199 1203 > EOF
1200 1204
1201 1205 Request to eventual cache URL should return 404 (validating the cache server works)
1202 1206
1203 1207 $ sendhttpraw << EOF
1204 1208 > httprequest GET api/simplecache/missingkey
1205 1209 > user-agent: test
1206 1210 > EOF
1207 1211 using raw connection to peer
1208 1212 s> GET /api/simplecache/missingkey HTTP/1.1\r\n
1209 1213 s> Accept-Encoding: identity\r\n
1210 1214 s> user-agent: test\r\n
1211 1215 s> host: $LOCALIP:$HGPORT\r\n (glob)
1212 1216 s> \r\n
1213 1217 s> makefile('rb', None)
1214 1218 s> HTTP/1.1 404 Not Found\r\n
1215 1219 s> Server: testing stub value\r\n
1216 1220 s> Date: $HTTP_DATE$\r\n
1217 1221 s> Content-Type: text/plain\r\n
1218 1222 s> Content-Length: 22\r\n
1219 1223 s> \r\n
1220 1224 s> key not found in cache
1221 1225
1222 1226 Send a cacheable request
1223 1227
1224 1228 $ sendhttpv2peer << EOF
1225 1229 > command manifestdata
1226 1230 > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41']
1227 1231 > tree eval:b''
1228 1232 > fields eval:[b'parents']
1229 1233 > EOF
1230 1234 creating http peer for wire protocol version 2
1231 1235 sending manifestdata command
1232 1236 response: gen[
1233 1237 {
1234 1238 b'totalitems': 1
1235 1239 },
1236 1240 {
1237 1241 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
1238 1242 b'parents': [
1239 1243 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
1240 1244 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
1241 1245 ]
1242 1246 }
1243 1247 ]
1244 1248
1245 1249 Cached entry should be available on server
1246 1250
1247 1251 $ sendhttpraw << EOF
1248 1252 > httprequest GET api/simplecache/64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca
1249 1253 > user-agent: test
1250 1254 > EOF
1251 1255 using raw connection to peer
1252 1256 s> GET /api/simplecache/64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca HTTP/1.1\r\n
1253 1257 s> Accept-Encoding: identity\r\n
1254 1258 s> user-agent: test\r\n
1255 1259 s> host: $LOCALIP:$HGPORT\r\n (glob)
1256 1260 s> \r\n
1257 1261 s> makefile('rb', None)
1258 1262 s> HTTP/1.1 200 OK\r\n
1259 1263 s> Server: testing stub value\r\n
1260 1264 s> Date: $HTTP_DATE$\r\n
1261 1265 s> Content-Type: application/mercurial-cbor\r\n
1262 1266 s> Content-Length: 91\r\n
1263 1267 s> \r\n
1264 1268 s> \xa1Jtotalitems\x01\xa2DnodeT\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&AGparents\x82T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
1265 1269 cbor> [
1266 1270 {
1267 1271 b'totalitems': 1
1268 1272 },
1269 1273 {
1270 1274 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
1271 1275 b'parents': [
1272 1276 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
1273 1277 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
1274 1278 ]
1275 1279 }
1276 1280 ]
1277 1281
1278 1282 2nd request should result in content redirect response
1279 1283
1280 1284 $ sendhttpv2peer << EOF
1281 1285 > command manifestdata
1282 1286 > nodes eval:[b'\x99\x2f\x47\x79\x02\x9a\x3d\xf8\xd0\x66\x6d\x00\xbb\x92\x4f\x69\x63\x4e\x26\x41']
1283 1287 > tree eval:b''
1284 1288 > fields eval:[b'parents']
1285 1289 > EOF
1286 1290 creating http peer for wire protocol version 2
1287 1291 sending manifestdata command
1288 1292 response: gen[
1289 1293 {
1290 1294 b'totalitems': 1
1291 1295 },
1292 1296 {
1293 1297 b'node': b'\x99/Gy\x02\x9a=\xf8\xd0fm\x00\xbb\x92OicN&A',
1294 1298 b'parents': [
1295 1299 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
1296 1300 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
1297 1301 ]
1298 1302 }
1299 1303 ]
1300 1304
1301 1305 $ cat error.log
1302 1306 $ killdaemons.py
1303 1307
1304 1308 $ cat .hg/blackbox.log
1305 1309 *> cacher constructed for manifestdata (glob)
1306 1310 *> cache miss for 64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca (glob)
1307 1311 *> storing cache entry for 64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca (glob)
1308 1312 *> cacher constructed for manifestdata (glob)
1309 1313 *> cache hit for 64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca (glob)
1310 1314 *> sending content redirect for 64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca to http://*:$HGPORT/api/simplecache/64b3162af49ea3c88e8ce2785e03ed7b88a2d6ca (glob)
General Comments 0
You need to be logged in to leave comments. Login now