comparison mercurial/httppeer.py @ 37552:8b8a845c85fc

httppeer: perform capabilities request in makepeer() Previously, we constructed an httppeer then always ran _fetchcaps() to issue the capabilities command. We want to issue the capabilities command before constructing a peer instance so we can construct an appropriate peer instance depending on the capabilities result. With the code for making and sending requests moved out of httppeer, it is now possible to send command requests without an httppeer. This commit creates a new function for making the capabilities request and calls it as part of makepeer(). This code should be functionality equivalent to what existed before. Differential Revision: https://phab.mercurial-scm.org/D3237
author Gregory Szorc <gregory.szorc@gmail.com>
date Tue, 10 Apr 2018 13:11:40 -0700
parents 946eb204ba67
children 6b08cf6b900f
comparison
equal deleted inserted replaced
37551:946eb204ba67 37552:8b8a845c85fc
364 resp = util.compengines['zlib'].decompressorreader(resp) 364 resp = util.compengines['zlib'].decompressorreader(resp)
365 365
366 return respurl, resp 366 return respurl, resp
367 367
368 class httppeer(wireproto.wirepeer): 368 class httppeer(wireproto.wirepeer):
369 def __init__(self, ui, path, url, opener, requestbuilder): 369 def __init__(self, ui, path, url, opener, requestbuilder, caps):
370 self.ui = ui 370 self.ui = ui
371 self._path = path 371 self._path = path
372 self._url = url 372 self._url = url
373 self._caps = None 373 self._caps = caps
374 self._urlopener = opener 374 self._urlopener = opener
375 self._requestbuilder = requestbuilder 375 self._requestbuilder = requestbuilder
376 376
377 def __del__(self): 377 def __del__(self):
378 for h in self._urlopener.handlers: 378 for h in self._urlopener.handlers:
399 # End of ipeerconnection interface. 399 # End of ipeerconnection interface.
400 400
401 # Begin of ipeercommands interface. 401 # Begin of ipeercommands interface.
402 402
403 def capabilities(self): 403 def capabilities(self):
404 # self._fetchcaps() should have been called as part of peer
405 # handshake. So self._caps should always be set.
406 assert self._caps is not None
407 return self._caps 404 return self._caps
408 405
409 # End of ipeercommands interface. 406 # End of ipeercommands interface.
410 407
411 # look up capabilities only when needed 408 # look up capabilities only when needed
412
413 def _fetchcaps(self):
414 self._caps = set(self._call('capabilities').split())
415 409
416 def _callstream(self, cmd, _compressible=False, **args): 410 def _callstream(self, cmd, _compressible=False, **args):
417 args = pycompat.byteskwargs(args) 411 args = pycompat.byteskwargs(args)
418 412
419 req, cu, qs = makev1commandrequest(self.ui, self._requestbuilder, 413 req, cu, qs = makev1commandrequest(self.ui, self._requestbuilder,
601 else: 595 else:
602 error.ProgrammingError('unhandled action: %s' % action) 596 error.ProgrammingError('unhandled action: %s' % action)
603 597
604 return results 598 return results
605 599
600 def performhandshake(ui, url, opener, requestbuilder):
601 # The handshake is a request to the capabilities command.
602
603 caps = None
604 def capable(x):
605 raise error.ProgrammingError('should not be called')
606
607 req, requrl, qs = makev1commandrequest(ui, requestbuilder, caps,
608 capable, url, 'capabilities',
609 {})
610
611 resp = sendrequest(ui, opener, req)
612
613 respurl, resp = parsev1commandresponse(ui, url, requrl, qs, resp,
614 compressible=False)
615
616 try:
617 rawcaps = resp.read()
618 finally:
619 resp.close()
620
621 return respurl, set(rawcaps.split())
622
606 def makepeer(ui, path, requestbuilder=urlreq.request): 623 def makepeer(ui, path, requestbuilder=urlreq.request):
607 """Construct an appropriate HTTP peer instance. 624 """Construct an appropriate HTTP peer instance.
608 625
609 ``requestbuilder`` is the type used for constructing HTTP requests. 626 ``requestbuilder`` is the type used for constructing HTTP requests.
610 It exists as an argument so extensions can override the default. 627 It exists as an argument so extensions can override the default.
618 url, authinfo = u.authinfo() 635 url, authinfo = u.authinfo()
619 ui.debug('using %s\n' % url) 636 ui.debug('using %s\n' % url)
620 637
621 opener = urlmod.opener(ui, authinfo) 638 opener = urlmod.opener(ui, authinfo)
622 639
623 return httppeer(ui, path, url, opener, requestbuilder) 640 respurl, caps = performhandshake(ui, url, opener, requestbuilder)
641
642 return httppeer(ui, path, respurl, opener, requestbuilder, caps)
624 643
625 def instance(ui, path, create): 644 def instance(ui, path, create):
626 if create: 645 if create:
627 raise error.Abort(_('cannot create new http repository')) 646 raise error.Abort(_('cannot create new http repository'))
628 try: 647 try:
629 if path.startswith('https:') and not urlmod.has_https: 648 if path.startswith('https:') and not urlmod.has_https:
630 raise error.Abort(_('Python support for SSL and HTTPS ' 649 raise error.Abort(_('Python support for SSL and HTTPS '
631 'is not installed')) 650 'is not installed'))
632 651
633 inst = makepeer(ui, path) 652 inst = makepeer(ui, path)
634 inst._fetchcaps()
635 653
636 return inst 654 return inst
637 except error.RepoError as httpexception: 655 except error.RepoError as httpexception:
638 try: 656 try:
639 r = statichttprepo.instance(ui, "static-" + path, create) 657 r = statichttprepo.instance(ui, "static-" + path, create)