##// END OF EJS Templates
Move import's working dir update code into patch.updatedir
Move import's working dir update code into patch.updatedir

File last commit:

r2859:345bac2b default
r2933:439fd013 default
Show More
httprepo.py
352 lines | 12.5 KiB | text/x-python | PythonLexer
mpm@selenic.com
Break apart hg.py...
r1089 # httprepo.py - HTTP repository proxy classes for mercurial
#
Vadim Gelfer
update copyrights.
r2859 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
mpm@selenic.com
Break apart hg.py...
r1089 #
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
from node import *
from remoterepo import *
Benoit Boissinot
i18n first part: make '_' available for files who need it
r1400 from i18n import gettext as _
Bryan O'Sullivan
Fix lots of exception-related problems....
r1251 from demandload import *
Thomas Arendsen Hein
Catch HTTPException when reading from remote http repository....
r2015 demandload(globals(), "hg os urllib urllib2 urlparse zlib util httplib")
Vadim Gelfer
http client: better work with authorization errors, broken sockets.
r2467 demandload(globals(), "errno keepalive tempfile socket")
mpm@selenic.com
Break apart hg.py...
r1089
Vadim Gelfer
httprepo: make "http://user:pass@host/" urls work
r2447 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
Vadim Gelfer
prompt user for http authentication info...
r2281 def __init__(self, ui):
Vadim Gelfer
httprepo: make "http://user:pass@host/" urls work
r2447 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
Vadim Gelfer
prompt user for http authentication info...
r2281 self.ui = ui
def find_user_password(self, realm, authuri):
Vadim Gelfer
httprepo: make "http://user:pass@host/" urls work
r2447 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
Vadim Gelfer
prompt user for http authentication info...
r2281 self, realm, authuri)
Alexis S. L. Carvalho
Allow http://user@example.com URLs (i.e. without passwords)
r2556 user, passwd = authinfo
if user and passwd:
return (user, passwd)
Vadim Gelfer
prompt user for http authentication info...
r2281
Vadim Gelfer
httprepo: fix small bug in authentication.
r2446 if not self.ui.interactive:
Vadim Gelfer
http: fix many problems with url parsing and auth. added proxy test....
r2337 raise util.Abort(_('http authorization required'))
Vadim Gelfer
prompt user for http authentication info...
r2281 self.ui.write(_("http authorization required\n"))
self.ui.status(_("realm: %s\n") % realm)
Alexis S. L. Carvalho
Allow http://user@example.com URLs (i.e. without passwords)
r2556 if user:
self.ui.status(_("user: %s\n") % user)
else:
user = self.ui.prompt(_("user:"), default=None)
if not passwd:
passwd = self.ui.getpass()
Vadim Gelfer
prompt user for http authentication info...
r2281
self.add_password(realm, authuri, user, passwd)
return (user, passwd)
Vadim Gelfer
http: fix many problems with url parsing and auth. added proxy test....
r2337 def netlocsplit(netloc):
'''split [user[:passwd]@]host[:port] into 4-tuple.'''
a = netloc.find('@')
if a == -1:
user, passwd = None, None
else:
userpass, netloc = netloc[:a], netloc[a+1:]
c = userpass.find(':')
if c == -1:
user, passwd = urllib.unquote(userpass), None
else:
user = urllib.unquote(userpass[:c])
passwd = urllib.unquote(userpass[c+1:])
c = netloc.find(':')
if c == -1:
host, port = netloc, None
else:
host, port = netloc[:c], netloc[c+1:]
return host, port, user, passwd
def netlocunsplit(host, port, user=None, passwd=None):
'''turn host, port, user, passwd into [user[:passwd]@]host[:port].'''
if port:
hostport = host + ':' + port
else:
hostport = host
if user:
if passwd:
userpass = urllib.quote(user) + ':' + urllib.quote(passwd)
else:
userpass = urllib.quote(user)
return userpass + '@' + hostport
return hostport
Vadim Gelfer
push over http: client support....
r2465 class httpconnection(keepalive.HTTPConnection):
# must be able to send big bundle as stream.
def send(self, data):
if isinstance(data, str):
keepalive.HTTPConnection.send(self, data)
else:
# if auth required, some data sent twice, so rewind here
data.seek(0)
for chunk in util.filechunkiter(data):
keepalive.HTTPConnection.send(self, chunk)
Alexis S. L. Carvalho
HTTPS: fix python2.3, persistent connections, don't explode if SSL is not available...
r2569 class basehttphandler(keepalive.HTTPHandler):
Vadim Gelfer
push over http: client support....
r2465 def http_open(self, req):
return self.do_open(httpconnection, req)
Alexis S. L. Carvalho
HTTPS: fix python2.3, persistent connections, don't explode if SSL is not available...
r2569 has_https = hasattr(urllib2, 'HTTPSHandler')
if has_https:
class httpsconnection(httplib.HTTPSConnection):
response_class = keepalive.HTTPResponse
# must be able to send big bundle as stream.
Alexis S. L. Carvalho
Fix push over https....
r2557
Alexis S. L. Carvalho
HTTPS: fix python2.3, persistent connections, don't explode if SSL is not available...
r2569 def send(self, data):
if isinstance(data, str):
httplib.HTTPSConnection.send(self, data)
else:
# if auth required, some data sent twice, so rewind here
data.seek(0)
for chunk in util.filechunkiter(data):
httplib.HTTPSConnection.send(self, chunk)
Alexis S. L. Carvalho
Fix push over https....
r2557
Alexis S. L. Carvalho
HTTPS: fix python2.3, persistent connections, don't explode if SSL is not available...
r2569 class httphandler(basehttphandler, urllib2.HTTPSHandler):
def https_open(self, req):
return self.do_open(httpsconnection, req)
else:
class httphandler(basehttphandler):
pass
Alexis S. L. Carvalho
Fix push over https....
r2557
mpm@selenic.com
Break apart hg.py...
r1089 class httprepository(remoterepository):
def __init__(self, ui, path):
Vadim Gelfer
hooks: add url to changegroup, incoming, prechangegroup, pretxnchangegroup hooks...
r2673 self.path = path
Vadim Gelfer
http: query server for capabilities
r2442 self.caps = None
Vadim Gelfer
http: fix many problems with url parsing and auth. added proxy test....
r2337 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
if query or frag:
raise util.Abort(_('unsupported URL component: "%s"') %
(query or frag))
if not urlpath: urlpath = '/'
host, port, user, passwd = netlocsplit(netloc)
# urllib cannot handle URLs with embedded user or passwd
Vadim Gelfer
hooks: add url to changegroup, incoming, prechangegroup, pretxnchangegroup hooks...
r2673 self._url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
urlpath, '', ''))
mpm@selenic.com
Break apart hg.py...
r1089 self.ui = ui
Vadim Gelfer
http: fix many problems with url parsing and auth. added proxy test....
r2337
proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
proxyauthinfo = None
Vadim Gelfer
push over http: client support....
r2465 handler = httphandler()
Vadim Gelfer
http: fix many problems with url parsing and auth. added proxy test....
r2337
if proxyurl:
# proxy can be proper url or host[:port]
if not (proxyurl.startswith('http:') or
proxyurl.startswith('https:')):
proxyurl = 'http://' + proxyurl + '/'
snpqf = urlparse.urlsplit(proxyurl)
proxyscheme, proxynetloc, proxypath, proxyquery, proxyfrag = snpqf
hpup = netlocsplit(proxynetloc)
proxyhost, proxyport, proxyuser, proxypasswd = hpup
if not proxyuser:
proxyuser = ui.config("http_proxy", "user")
proxypasswd = ui.config("http_proxy", "passwd")
mpm@selenic.com
Break apart hg.py...
r1089
Vadim Gelfer
http: fix many problems with url parsing and auth. added proxy test....
r2337 # see if we should use a proxy for this url
no_list = [ "localhost", "127.0.0.1" ]
Thomas Arendsen Hein
Make "[web] allow_push, deny_push" and "[http_proxy] no" use ui.configlist.
r2501 no_list.extend([p.lower() for
p in ui.configlist("http_proxy", "no")])
Vadim Gelfer
http: fix many problems with url parsing and auth. added proxy test....
r2337 no_list.extend([p.strip().lower() for
p in os.getenv("no_proxy", '').split(',')
if p.strip()])
# "http_proxy.always" config is for running tests on localhost
if (not ui.configbool("http_proxy", "always") and
host.lower() in no_list):
ui.debug(_('disabling proxy for %s\n') % host)
else:
proxyurl = urlparse.urlunsplit((
proxyscheme, netlocunsplit(proxyhost, proxyport,
proxyuser, proxypasswd or ''),
proxypath, proxyquery, proxyfrag))
handler = urllib2.ProxyHandler({scheme: proxyurl})
ui.debug(_('proxying through %s\n') % proxyurl)
mpm@selenic.com
Break apart hg.py...
r1089
Vadim Gelfer
http: fix many problems with url parsing and auth. added proxy test....
r2337 # urllib2 takes proxy values from the environment and those
# will take precedence if found, so drop them
mpm@selenic.com
Break apart hg.py...
r1089 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
try:
if os.environ.has_key(env):
del os.environ[env]
except OSError:
pass
Vadim Gelfer
http: fix many problems with url parsing and auth. added proxy test....
r2337 passmgr = passwordmgr(ui)
if user:
Alexis S. L. Carvalho
Allow http://user@example.com URLs (i.e. without passwords)
r2556 ui.debug(_('http auth: user %s, password %s\n') %
(user, passwd and '*' * len(passwd) or 'not set'))
Vadim Gelfer
http: fix many problems with url parsing and auth. added proxy test....
r2337 passmgr.add_password(None, host, user, passwd or '')
mpm@selenic.com
Break apart hg.py...
r1089
Vadim Gelfer
http: fix many problems with url parsing and auth. added proxy test....
r2337 opener = urllib2.build_opener(
handler,
urllib2.HTTPBasicAuthHandler(passmgr),
urllib2.HTTPDigestAuthHandler(passmgr))
Vadim Gelfer
prompt user for http authentication info...
r2281
Matt Mackall
Set the user agent for httprepo communication
r1359 # 1.0 here is the _protocol_ version
opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
mpm@selenic.com
Break apart hg.py...
r1089 urllib2.install_opener(opener)
Vadim Gelfer
hooks: add url to changegroup, incoming, prechangegroup, pretxnchangegroup hooks...
r2673 def url(self):
return self.path
Vadim Gelfer
http: query server for capabilities
r2442 # look up capabilities only when needed
def get_caps(self):
if self.caps is None:
try:
self.caps = self.do_read('capabilities').split()
except hg.RepoError:
self.caps = ()
Vadim Gelfer
push over http: client support....
r2465 self.ui.debug(_('capabilities: %s\n') %
(' '.join(self.caps or ['none'])))
Vadim Gelfer
http: query server for capabilities
r2442 return self.caps
capabilities = property(get_caps)
Vadim Gelfer
make push over http print good error message.
r1870 def lock(self):
raise util.Abort(_('operation not supported over http'))
mpm@selenic.com
Break apart hg.py...
r1089 def do_cmd(self, cmd, **args):
Vadim Gelfer
push over http: client support....
r2465 data = args.pop('data', None)
headers = args.pop('headers', {})
Benoit Boissinot
i18n part2: use '_' for all strings who are part of the user interface
r1402 self.ui.debug(_("sending %s command\n") % cmd)
mpm@selenic.com
Break apart hg.py...
r1089 q = {"cmd": cmd}
q.update(args)
qs = urllib.urlencode(q)
Vadim Gelfer
hooks: add url to changegroup, incoming, prechangegroup, pretxnchangegroup hooks...
r2673 cu = "%s?%s" % (self._url, qs)
Thomas Arendsen Hein
Catch urllib's HTTPException and give a meaningful error message to the user....
r2294 try:
Vadim Gelfer
push over http: client support....
r2465 resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
Vadim Gelfer
http client: better work with authorization errors, broken sockets.
r2467 except urllib2.HTTPError, inst:
if inst.code == 401:
raise util.Abort(_('authorization failed'))
raise
Thomas Arendsen Hein
Catch urllib's HTTPException and give a meaningful error message to the user....
r2294 except httplib.HTTPException, inst:
Vadim Gelfer
http: print better error if exception happens.
r2336 self.ui.debug(_('http error while sending %s command\n') % cmd)
self.ui.print_exc()
raise IOError(None, inst)
Vadim Gelfer
http client: support persistent connections....
r2435 try:
proto = resp.getheader('content-type')
except AttributeError:
proto = resp.headers['content-type']
mpm@selenic.com
Break apart hg.py...
r1089
# accept old "text/plain" and "application/hg-changegroup" for now
if not proto.startswith('application/mercurial') and \
not proto.startswith('text/plain') and \
not proto.startswith('application/hg-changegroup'):
Benoit Boissinot
i18n part2: use '_' for all strings who are part of the user interface
r1402 raise hg.RepoError(_("'%s' does not appear to be an hg repository") %
Vadim Gelfer
hooks: add url to changegroup, incoming, prechangegroup, pretxnchangegroup hooks...
r2673 self._url)
mpm@selenic.com
Break apart hg.py...
r1089
if proto.startswith('application/mercurial'):
version = proto[22:]
if float(version) > 0.1:
Benoit Boissinot
i18n part2: use '_' for all strings who are part of the user interface
r1402 raise hg.RepoError(_("'%s' uses newer protocol %s") %
Vadim Gelfer
hooks: add url to changegroup, incoming, prechangegroup, pretxnchangegroup hooks...
r2673 (self._url, version))
mpm@selenic.com
Break apart hg.py...
r1089
return resp
Vadim Gelfer
http client: support persistent connections....
r2435 def do_read(self, cmd, **args):
fp = self.do_cmd(cmd, **args)
try:
return fp.read()
finally:
# if using keepalive, allow connection to be reused
fp.close()
mpm@selenic.com
Break apart hg.py...
r1089 def heads(self):
Vadim Gelfer
http client: support persistent connections....
r2435 d = self.do_read("heads")
mpm@selenic.com
Break apart hg.py...
r1089 try:
return map(bin, d[:-1].split(" "))
except:
Benoit Boissinot
i18n part2: use '_' for all strings who are part of the user interface
r1402 self.ui.warn(_("unexpected response:\n") + d[:400] + "\n...\n")
mpm@selenic.com
Break apart hg.py...
r1089 raise
def branches(self, nodes):
n = " ".join(map(hex, nodes))
Vadim Gelfer
http client: support persistent connections....
r2435 d = self.do_read("branches", nodes=n)
mpm@selenic.com
Break apart hg.py...
r1089 try:
br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
return br
except:
Benoit Boissinot
i18n part2: use '_' for all strings who are part of the user interface
r1402 self.ui.warn(_("unexpected response:\n") + d[:400] + "\n...\n")
mpm@selenic.com
Break apart hg.py...
r1089 raise
def between(self, pairs):
n = "\n".join(["-".join(map(hex, p)) for p in pairs])
Vadim Gelfer
http client: support persistent connections....
r2435 d = self.do_read("between", pairs=n)
mpm@selenic.com
Break apart hg.py...
r1089 try:
p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
return p
except:
Benoit Boissinot
i18n part2: use '_' for all strings who are part of the user interface
r1402 self.ui.warn(_("unexpected response:\n") + d[:400] + "\n...\n")
mpm@selenic.com
Break apart hg.py...
r1089 raise
Vadim Gelfer
add preoutgoing and outgoing hooks....
r1736 def changegroup(self, nodes, kind):
mpm@selenic.com
Break apart hg.py...
r1089 n = " ".join(map(hex, nodes))
f = self.do_cmd("changegroup", roots=n)
bytes = 0
Eric Hopper
Fix same performance bug as c3654cfaa77 but for httprepo.py instead.
r1376 def zgenerator(f):
zd = zlib.decompressobj()
Thomas Arendsen Hein
Catch HTTPException when reading from remote http repository....
r2015 try:
for chnk in f:
yield zd.decompress(chnk)
except httplib.HTTPException, inst:
raise IOError(None, _('connection ended unexpectedly'))
Eric Hopper
Fix same performance bug as c3654cfaa77 but for httprepo.py instead.
r1376 yield zd.flush()
mpm@selenic.com
Break apart hg.py...
r1089
Eric Hopper
Fix same performance bug as c3654cfaa77 but for httprepo.py instead.
r1376 return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
mpm@selenic.com
Break apart hg.py...
r1089
Vadim Gelfer
extend network protocol to stop clients from locking servers...
r2439 def unbundle(self, cg, heads, source):
Vadim Gelfer
push over http: client support....
r2465 # have to stream bundle to a temp file because we do not have
# http 1.1 chunked transfer.
fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
fp = os.fdopen(fd, 'wb+')
try:
for chunk in util.filechunkiter(cg):
fp.write(chunk)
length = fp.tell()
try:
Vadim Gelfer
http client: better work with authorization errors, broken sockets.
r2467 rfp = self.do_cmd(
'unbundle', data=fp,
headers={'content-length': length,
'content-type': 'application/octet-stream'},
heads=' '.join(map(hex, heads)))
try:
ret = int(rfp.readline())
self.ui.write(rfp.read())
return ret
finally:
rfp.close()
except socket.error, err:
if err[0] in (errno.ECONNRESET, errno.EPIPE):
raise util.Abort(_('push failed: %s'), err[1])
raise util.Abort(err[1])
Vadim Gelfer
push over http: client support....
r2465 finally:
fp.close()
os.unlink(tempname)
Vadim Gelfer
extend network protocol to stop clients from locking servers...
r2439
Vadim Gelfer
add support for streaming clone....
r2612 def stream_out(self):
return self.do_cmd('stream_out')
mpm@selenic.com
Break apart hg.py...
r1089 class httpsrepository(httprepository):
Alexis S. L. Carvalho
HTTPS: fix python2.3, persistent connections, don't explode if SSL is not available...
r2569 def __init__(self, ui, path):
if not has_https:
raise util.Abort(_('Python support for SSL and HTTPS '
'is not installed'))
httprepository.__init__(self, ui, path)
Vadim Gelfer
clean up hg.py: move repo constructor code into each repo module
r2740
def instance(ui, path, create):
if create:
raise util.Abort(_('cannot create new http repository'))
if path.startswith('hg:'):
ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
path = 'http:' + path[3:]
if path.startswith('https:'):
return httpsrepository(ui, path)
return httprepository(ui, path)