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) |