##// END OF EJS Templates
merge with crew
merge with crew

File last commit:

r2447:cd00531e default
r2459:5c5277f0 merge default
Show More
httprepo.py
255 lines | 9.0 KiB | text/x-python | PythonLexer
mpm@selenic.com
Break apart hg.py...
r1089 # httprepo.py - HTTP repository proxy classes for mercurial
#
# Copyright 2005 Matt Mackall <mpm@selenic.com>
#
# 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: support persistent connections....
r2435 demandload(globals(), "keepalive")
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)
if authinfo != (None, None):
return authinfo
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)
user = self.ui.prompt(_("user:"), default=None)
passwd = self.ui.getpass()
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
mpm@selenic.com
Break apart hg.py...
r1089 class httprepository(remoterepository):
def __init__(self, ui, 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
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
http client: support persistent connections....
r2435 handler = keepalive.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" ]
no_list.extend([p.strip().lower() for
p in ui.config("http_proxy", "no", '').split(',')
if p.strip()])
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:
Vadim Gelfer
httprepo: make "http://user:pass@host/" urls work
r2447 ui.debug(_('will use user %s, password %s for http auth\n') %
(user, '*' * len(passwd)))
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
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 = ()
return self.caps
capabilities = property(get_caps)
mpm@selenic.com
Break apart hg.py...
r1089 def dev(self):
return -1
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):
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)
cu = "%s?%s" % (self.url, qs)
Thomas Arendsen Hein
Catch urllib's HTTPException and give a meaningful error message to the user....
r2294 try:
resp = urllib2.urlopen(cu)
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") %
Bryan O'Sullivan
Fix lots of exception-related problems....
r1251 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") %
Bryan O'Sullivan
Fix lots of exception-related problems....
r1251 (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):
raise util.Abort(_('operation not supported over http'))
mpm@selenic.com
Break apart hg.py...
r1089 class httpsrepository(httprepository):
pass