##// END OF EJS Templates
Merge with stable
Merge with stable

File last commit:

r9881:54b518fc default
r10207:f5e55f1c merge default
Show More
httprepo.py
267 lines | 9.7 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 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.
mpm@selenic.com
Break apart hg.py...
r1089
Matt Mackall
Autodetect static-http
r7211 from node import bin, hex, nullid
Matt Mackall
Simplify i18n imports
r3891 from i18n import _
Simon Heimberg
separate import lines from mercurial and general python modules
r8312 import repo, changegroup, statichttprepo, error, url, util
import os, urllib, urllib2, urlparse, zlib, httplib
import errno, socket
Henrik Stuart
support encoding fallback in branchmap to maintain compatibility with 1.3.x
r9861 import encoding
Alexis S. L. Carvalho
Work around urllib2 digest auth bug with Python < 2.5...
r4678
Matt Mackall
remove duplicate zgenerator in httprepo
r3661 def zgenerator(f):
zd = zlib.decompressobj()
try:
for chunk in util.filechunkiter(f):
yield zd.decompress(chunk)
Benoit Boissinot
remove unused variables
r7280 except httplib.HTTPException:
Matt Mackall
remove duplicate zgenerator in httprepo
r3661 raise IOError(None, _('connection ended unexpectedly'))
yield zd.flush()
Matt Mackall
remoterepo: no longer needed...
r6313 class httprepository(repo.repository):
mpm@selenic.com
Break apart hg.py...
r1089 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
Andrei Vermel
Close keepalive connections to fix server traceback
r4132 self.handler = 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))
# urllib cannot handle URLs with embedded user or passwd
Benoit Boissinot
factor out the url handling from httprepo...
r7270 self._url, authinfo = url.getauthinfo(path)
mpm@selenic.com
Break apart hg.py...
r1089 self.ui = ui
Martin Geisler
do not attempt to translate ui.debug output
r9467 self.ui.debug('using %s\n' % self._url)
Vadim Gelfer
http: fix many problems with url parsing and auth. added proxy test....
r2337
Benoit Boissinot
factor out the url handling from httprepo...
r7270 self.urlopener = url.opener(ui, authinfo)
Thomas Arendsen Hein
Removed trailing whitespace and tabs from python files
r4516
Steve Borho
close sockets on httprepository deletion (issue1487)...
r7752 def __del__(self):
for h in self.urlopener.handlers:
h.close()
if hasattr(h, "close_all"):
h.close_all()
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:
Martin Geisler
util: use built-in set and frozenset...
r8150 self.caps = set(self.do_read('capabilities').split())
Matt Mackall
error: move repo errors...
r7637 except error.RepoError:
Martin Geisler
util: use built-in set and frozenset...
r8150 self.caps = set()
Martin Geisler
do not attempt to translate ui.debug output
r9467 self.ui.debug('capabilities: %s\n' %
Vadim Gelfer
push over http: client support....
r2465 (' '.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', {})
Martin Geisler
do not attempt to translate ui.debug output
r9467 self.ui.debug("sending %s command\n" % cmd)
mpm@selenic.com
Break apart hg.py...
r1089 q = {"cmd": cmd}
q.update(args)
Benoit Boissinot
httprepo: record the url after a request, makes pull + redirect works...
r3562 qs = '?%s' % 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:
Thomas Arendsen Hein
Turn bundle file into a string for http push, for urllib2 over proxies.
r3567 if data:
Martin Geisler
do not attempt to translate ui.debug output
r9467 self.ui.debug("sending %s bytes\n" % len(data))
Benoit Boissinot
factor out the url handling from httprepo...
r7270 resp = self.urlopener.open(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:
Martin Geisler
do not attempt to translate ui.debug output
r9467 self.ui.debug('http error while sending %s command\n' % cmd)
Matt Mackall
ui: print_exc() -> traceback()
r8206 self.ui.traceback()
Vadim Gelfer
http: print better error if exception happens.
r2336 raise IOError(None, inst)
Thomas Arendsen Hein
Catch python2.3's IndexError with bogus http proxy settings. (issue203)
r3399 except IndexError:
# this only happens with Python 2.3, later versions raise URLError
raise util.Abort(_('http error, possibly caused by proxy setting'))
Benoit Boissinot
httprepo: record the url after a request, makes pull + redirect works...
r3562 # record the url we got redirected to
Thomas Arendsen Hein
Inform the user about the new URL when being redirected via http....
r3570 resp_url = resp.geturl()
if resp_url.endswith(qs):
resp_url = resp_url[:-len(qs)]
Dan Villiom Podlaski Christiansen
httprepo: suppress the `real URL is...' message in safe, common cases....
r9881 if self._url.rstrip('/') != resp_url.rstrip('/'):
Thomas Arendsen Hein
Inform the user about the new URL when being redirected via http....
r3570 self.ui.status(_('real URL is %s\n') % resp_url)
self._url = resp_url
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
Steve Borho
hide passwords in httprepo error messages
r8053 safeurl = url.hidepassword(self._url)
mpm@selenic.com
Break apart hg.py...
r1089 # accept old "text/plain" and "application/hg-changegroup" for now
Thomas Arendsen Hein
Cleanup of whitespace, indentation and line continuation.
r4633 if not (proto.startswith('application/mercurial-') or
proto.startswith('text/plain') or
proto.startswith('application/hg-changegroup')):
Martin Geisler
do not attempt to translate ui.debug output
r9467 self.ui.debug("requested URL: '%s'\n" % url.hidepassword(cu))
Jesse Glick
Sometimes pushing to an HTTP repo fails with a useless message....
r9748 raise error.RepoError(_("'%s' does not appear to be an hg repository:\n"
"---%%<--- (%s)\n%s\n---%%<---\n")
% (safeurl, proto, resp.read()))
mpm@selenic.com
Break apart hg.py...
r1089
Benoit Boissinot
fix handling of multiple Content-type headers...
r4012 if proto.startswith('application/mercurial-'):
try:
Thomas Arendsen Hein
Avoid float rounding errors when checking http protocol version.
r4356 version = proto.split('-', 1)[1]
version_info = tuple([int(n) for n in version.split('.')])
Benoit Boissinot
fix handling of multiple Content-type headers...
r4012 except ValueError:
Matt Mackall
error: move repo errors...
r7637 raise error.RepoError(_("'%s' sent a broken Content-Type "
Steve Borho
hide passwords in httprepo error messages
r8053 "header (%s)") % (safeurl, proto))
Thomas Arendsen Hein
Avoid float rounding errors when checking http protocol version.
r4356 if version_info > (0, 1):
Matt Mackall
error: move repo errors...
r7637 raise error.RepoError(_("'%s' uses newer protocol %s") %
Steve Borho
hide passwords in httprepo error messages
r8053 (safeurl, 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()
Eric Hopper
Adding changegroupsubset and lookup to web protocol so pull -r and...
r3444 def lookup(self, key):
Bryan O'Sullivan
Push capability checking into protocol-level code.
r5259 self.requirecap('lookup', _('look up remote revision'))
Matt Mackall
httprepo: add support for passing lookup exception data
r3445 d = self.do_cmd("lookup", key = key).read()
success, data = d[:-1].split(' ', 1)
if int(success):
return bin(data)
Matt Mackall
error: move repo errors...
r7637 raise error.RepoError(data)
Eric Hopper
Adding changegroupsubset and lookup to web protocol so pull -r and...
r3444
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:
Matt Mackall
error: move UnexpectedOutput (now ResponseError)
r7641 raise error.ResponseError(_("unexpected response:"), d)
mpm@selenic.com
Break apart hg.py...
r1089
Henrik Stuart
named branches: client branchmap wire protocol support (issue736)...
r8563 def branchmap(self):
d = self.do_read("branchmap")
try:
branchmap = {}
for branchpart in d.splitlines():
branchheads = branchpart.split(' ')
branchname = urllib.unquote(branchheads[0])
Sune Foldager
branchmap: fix defective fallback fix 0262bb59016f...
r9878 # Earlier servers (1.3.x) send branch names in (their) local
# charset. The best we can do is assume it's identical to our
# own local charset, in case it's not utf-8.
Henrik Stuart
support encoding fallback in branchmap to maintain compatibility with 1.3.x
r9861 try:
Sune Foldager
branchmap: fix defective fallback fix 0262bb59016f...
r9878 branchname.decode('utf-8')
Henrik Stuart
support encoding fallback in branchmap to maintain compatibility with 1.3.x
r9861 except UnicodeDecodeError:
Sune Foldager
branchmap: fix defective fallback fix 0262bb59016f...
r9878 branchname = encoding.fromlocal(branchname)
Henrik Stuart
named branches: client branchmap wire protocol support (issue736)...
r8563 branchheads = [bin(x) for x in branchheads[1:]]
branchmap[branchname] = branchheads
return branchmap
except:
raise error.ResponseError(_("unexpected response:"), d)
mpm@selenic.com
Break apart hg.py...
r1089 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:
Matt Mackall
error: move UnexpectedOutput (now ResponseError)
r7641 raise error.ResponseError(_("unexpected response:"), d)
mpm@selenic.com
Break apart hg.py...
r1089
def between(self, pairs):
Matt Mackall
protocol: avoid sending outrageously large between requests
r7342 batch = 8 # avoid giant requests
r = []
for i in xrange(0, len(pairs), batch):
n = " ".join(["-".join(map(hex, p)) for p in pairs[i:i + batch]])
d = self.do_read("between", pairs=n)
try:
r += [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
except:
Matt Mackall
error: move UnexpectedOutput (now ResponseError)
r7641 raise error.ResponseError(_("unexpected response:"), d)
Matt Mackall
protocol: avoid sending outrageously large between requests
r7342 return r
mpm@selenic.com
Break apart hg.py...
r1089
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)
Matt Mackall
remove duplicate zgenerator in httprepo
r3661 return util.chunkbuffer(zgenerator(f))
Eric Hopper
Adding changegroupsubset and lookup to web protocol so pull -r and...
r3444
def changegroupsubset(self, bases, heads, source):
Bryan O'Sullivan
Push capability checking into protocol-level code.
r5259 self.requirecap('changegroupsubset', _('look up remote changes'))
Eric Hopper
Adding changegroupsubset and lookup to web protocol so pull -r and...
r3444 baselst = " ".join([hex(n) for n in bases])
headlst = " ".join([hex(n) for n in heads])
f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst)
Matt Mackall
remove duplicate zgenerator in httprepo
r3661 return util.chunkbuffer(zgenerator(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.
Matt Mackall
unduplicate bundle writing code from httprepo
r3662 type = ""
types = self.capable('unbundle')
Alexis S. L. Carvalho
fix push over HTTP to older servers
r3703 # servers older than d1b16a746db6 will send 'unbundle' as a
# boolean capability
try:
types = types.split(',')
except AttributeError:
types = [""]
Matt Mackall
unduplicate bundle writing code from httprepo
r3662 if types:
Alexis S. L. Carvalho
fix push over HTTP to older servers
r3703 for x in types:
Matt Mackall
unduplicate bundle writing code from httprepo
r3662 if x in changegroup.bundletypes:
type = x
break
Thomas Arendsen Hein
Client support for hgweb unbundle with versions.
r3613
Matt Mackall
unduplicate bundle writing code from httprepo
r3662 tempname = changegroup.writebundle(cg, None, type)
Benoit Boissinot
factor out the url handling from httprepo...
r7270 fp = url.httpsendfile(tempname, "rb")
Vadim Gelfer
push over http: client support....
r2465 try:
try:
Benoit Boissinot
enhance the error output in case of failure during http push
r7010 resp = self.do_read(
'unbundle', data=fp,
headers={'Content-Type': 'application/octet-stream'},
heads=' '.join(map(hex, heads)))
resp_code, output = resp.split('\n', 1)
Vadim Gelfer
http client: better work with authorization errors, broken sockets.
r2467 try:
Benoit Boissinot
enhance the error output in case of failure during http push
r7010 ret = int(resp_code)
except ValueError, err:
Matt Mackall
error: move UnexpectedOutput (now ResponseError)
r7641 raise error.ResponseError(
Benoit Boissinot
enhance the error output in case of failure during http push
r7010 _('push failed (unexpected response):'), resp)
self.ui.write(output)
return ret
Vadim Gelfer
http client: better work with authorization errors, broken sockets.
r2467 except socket.error, err:
if err[0] in (errno.ECONNRESET, errno.EPIPE):
Thomas Arendsen Hein
Never apply string formatting to generated errors with util.Abort....
r3072 raise util.Abort(_('push failed: %s') % err[1])
Vadim Gelfer
http client: better work with authorization errors, broken sockets.
r2467 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):
Benoit Boissinot
Fix https availability checking...
r7279 if not url.has_https:
Alexis S. L. Carvalho
HTTPS: fix python2.3, persistent connections, don't explode if SSL is not available...
r2569 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'))
Matt Mackall
Autodetect static-http
r7211 try:
if path.startswith('https:'):
inst = httpsrepository(ui, path)
else:
inst = httprepository(ui, path)
inst.between([(nullid, nullid)])
return inst
Matt Mackall
error: move repo errors...
r7637 except error.RepoError:
Matt Mackall
Autodetect static-http
r7211 ui.note('(falling back to static-http)\n')
return statichttprepo.instance(ui, "static-" + path, create)