Peer instances are supposed to conform to a well-defined API so
consumers can be agnostic about the underlying peer type.
To reinforce this, this commit renames a handful of instance
attributes on httpeer so they no longer have a "public" name.
( )
hg-reviewers |
Peer instances are supposed to conform to a well-defined API so
consumers can be agnostic about the underlying peer type.
To reinforce this, this commit renames a handful of instance
attributes on httpeer so they no longer have a "public" name.
Lint Skipped |
Unit Tests Skipped |
hint=_('this may be an intermittent network failure; ' | hint=_('this may be an intermittent network failure; ' | ||||
'if the error persists, consider contacting the ' | 'if the error persists, consider contacting the ' | ||||
'network or server operator')) | 'network or server operator')) | ||||
resp.__class__ = readerproxy | resp.__class__ = readerproxy | ||||
class httppeer(wireproto.wirepeer): | class httppeer(wireproto.wirepeer): | ||||
def __init__(self, ui, path): | def __init__(self, ui, path): | ||||
self.path = path | self._path = path | ||||
self.caps = None | self._caps = None | ||||
self.urlopener = None | self._urlopener = None | ||||
self.requestbuilder = None | self._requestbuilder = None | ||||
u = util.url(path) | u = util.url(path) | ||||
if u.query or u.fragment: | if u.query or u.fragment: | ||||
raise error.Abort(_('unsupported URL component: "%s"') % | raise error.Abort(_('unsupported URL component: "%s"') % | ||||
(u.query or u.fragment)) | (u.query or u.fragment)) | ||||
# urllib cannot handle URLs with embedded user or passwd | # urllib cannot handle URLs with embedded user or passwd | ||||
self._url, authinfo = u.authinfo() | self._url, authinfo = u.authinfo() | ||||
self.ui = ui | self.ui = ui | ||||
self.ui.debug('using %s\n' % self._url) | self.ui.debug('using %s\n' % self._url) | ||||
self.urlopener = url.opener(ui, authinfo) | self._urlopener = url.opener(ui, authinfo) | ||||
self.requestbuilder = urlreq.request | self._requestbuilder = urlreq.request | ||||
def __del__(self): | def __del__(self): | ||||
urlopener = getattr(self, 'urlopener', None) | urlopener = getattr(self, '_urlopener', None) | ||||
if urlopener: | if urlopener: | ||||
for h in urlopener.handlers: | for h in urlopener.handlers: | ||||
h.close() | h.close() | ||||
getattr(h, "close_all", lambda : None)() | getattr(h, "close_all", lambda : None)() | ||||
def url(self): | def url(self): | ||||
return self.path | return self._path | ||||
# look up capabilities only when needed | # look up capabilities only when needed | ||||
def _fetchcaps(self): | def _fetchcaps(self): | ||||
self.caps = set(self._call('capabilities').split()) | self._caps = set(self._call('capabilities').split()) | ||||
def _capabilities(self): | def _capabilities(self): | ||||
if self.caps is None: | if self._caps is None: | ||||
try: | try: | ||||
self._fetchcaps() | self._fetchcaps() | ||||
except error.RepoError: | except error.RepoError: | ||||
self.caps = set() | self._caps = set() | ||||
self.ui.debug('capabilities: %s\n' % | self.ui.debug('capabilities: %s\n' % | ||||
(' '.join(self.caps or ['none']))) | (' '.join(self._caps or ['none']))) | ||||
return self.caps | return self._caps | ||||
def _callstream(self, cmd, _compressible=False, **args): | def _callstream(self, cmd, _compressible=False, **args): | ||||
if cmd == 'pushkey': | if cmd == 'pushkey': | ||||
args['data'] = '' | args['data'] = '' | ||||
data = args.pop('data', None) | data = args.pop('data', None) | ||||
headers = args.pop('headers', {}) | headers = args.pop('headers', {}) | ||||
self.ui.debug("sending %s command\n" % cmd) | self.ui.debug("sending %s command\n" % cmd) | ||||
q = [('cmd', cmd)] | q = [('cmd', cmd)] | ||||
headersize = 0 | headersize = 0 | ||||
varyheaders = [] | varyheaders = [] | ||||
# Important: don't use self.capable() here or else you end up | # Important: don't use self.capable() here or else you end up | ||||
# with infinite recursion when trying to look up capabilities | # with infinite recursion when trying to look up capabilities | ||||
# for the first time. | # for the first time. | ||||
postargsok = self.caps is not None and 'httppostargs' in self.caps | postargsok = self._caps is not None and 'httppostargs' in self._caps | ||||
# TODO: support for httppostargs when data is a file-like | # TODO: support for httppostargs when data is a file-like | ||||
# object rather than a basestring | # object rather than a basestring | ||||
canmungedata = not data or isinstance(data, basestring) | canmungedata = not data or isinstance(data, basestring) | ||||
if postargsok and canmungedata: | if postargsok and canmungedata: | ||||
strargs = urlreq.urlencode(sorted(args.items())) | strargs = urlreq.urlencode(sorted(args.items())) | ||||
if strargs: | if strargs: | ||||
if not data: | if not data: | ||||
data = strargs | data = strargs | ||||
headers['Content-Type'] = 'application/mercurial-0.1' | headers['Content-Type'] = 'application/mercurial-0.1' | ||||
# Tell the server we accept application/mercurial-0.2 and multiple | # Tell the server we accept application/mercurial-0.2 and multiple | ||||
# compression formats if the server is capable of emitting those | # compression formats if the server is capable of emitting those | ||||
# payloads. | # payloads. | ||||
protoparams = [] | protoparams = [] | ||||
mediatypes = set() | mediatypes = set() | ||||
if self.caps is not None: | if self._caps is not None: | ||||
mt = self.capable('httpmediatype') | mt = self.capable('httpmediatype') | ||||
if mt: | if mt: | ||||
protoparams.append('0.1') | protoparams.append('0.1') | ||||
mediatypes = set(mt.split(',')) | mediatypes = set(mt.split(',')) | ||||
if '0.2tx' in mediatypes: | if '0.2tx' in mediatypes: | ||||
protoparams.append('0.2') | protoparams.append('0.2') | ||||
headersize or 1024) | headersize or 1024) | ||||
for header, value in protoheaders: | for header, value in protoheaders: | ||||
headers[header] = value | headers[header] = value | ||||
varyheaders.append(header) | varyheaders.append(header) | ||||
if varyheaders: | if varyheaders: | ||||
headers['Vary'] = ','.join(varyheaders) | headers['Vary'] = ','.join(varyheaders) | ||||
req = self.requestbuilder(cu, data, headers) | req = self._requestbuilder(cu, data, headers) | ||||
if data is not None: | if data is not None: | ||||
self.ui.debug("sending %s bytes\n" % size) | self.ui.debug("sending %s bytes\n" % size) | ||||
req.add_unredirected_header('Content-Length', '%d' % size) | req.add_unredirected_header('Content-Length', '%d' % size) | ||||
try: | try: | ||||
resp = self.urlopener.open(req) | resp = self._urlopener.open(req) | ||||
except urlerr.httperror as inst: | except urlerr.httperror as inst: | ||||
if inst.code == 401: | if inst.code == 401: | ||||
raise error.Abort(_('authorization failed')) | raise error.Abort(_('authorization failed')) | ||||
raise | raise | ||||
except httplib.HTTPException as inst: | except httplib.HTTPException as inst: | ||||
self.ui.debug('http error while sending %s command\n' % cmd) | self.ui.debug('http error while sending %s command\n' % cmd) | ||||
self.ui.traceback() | self.ui.traceback() | ||||
raise IOError(None, inst) | raise IOError(None, inst) |