statichttprepo.py
227 lines
| 7.0 KiB
| text/x-python
|
PythonLexer
/ mercurial / statichttprepo.py
|
r1101 | # statichttprepo.py - simple http repository class for mercurial | ||
# | ||||
# This provides read-only repo access to repositories exported via static http | ||||
# | ||||
|
r4635 | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | ||
|
r1101 | # | ||
|
r8225 | # This software may be used and distributed according to the terms of the | ||
|
r10263 | # GNU General Public License version 2 or any later version. | ||
|
r1101 | |||
|
r25978 | from __future__ import absolute_import | ||
import errno | ||||
from .i18n import _ | ||||
from . import ( | ||||
changelog, | ||||
error, | ||||
localrepo, | ||||
manifest, | ||||
namespaces, | ||||
|
r34944 | pathutil, | ||
|
r25978 | url, | ||
util, | ||||
|
r31241 | vfs as vfsmod, | ||
|
r25978 | ) | ||
|
r1325 | |||
|
r28883 | urlerr = util.urlerr | ||
urlreq = util.urlreq | ||||
|
r7274 | class httprangereader(object): | ||
def __init__(self, url, opener): | ||||
# we assume opener has HTTPRangeHandler | ||||
self.url = url | ||||
self.pos = 0 | ||||
self.opener = opener | ||||
|
r11066 | self.name = url | ||
|
r27705 | |||
def __enter__(self): | ||||
return self | ||||
def __exit__(self, exc_type, exc_value, traceback): | ||||
self.close() | ||||
|
r7274 | def seek(self, pos): | ||
self.pos = pos | ||||
def read(self, bytes=None): | ||||
|
r28883 | req = urlreq.request(self.url) | ||
|
r7274 | end = '' | ||
if bytes: | ||||
end = self.pos + bytes - 1 | ||||
|
r16882 | if self.pos or end: | ||
req.add_header('Range', 'bytes=%d-%s' % (self.pos, end)) | ||||
|
r7274 | |||
|
r1325 | try: | ||
|
r7274 | f = self.opener.open(req) | ||
data = f.read() | ||||
|
r25196 | code = f.code | ||
|
r28883 | except urlerr.httperror as inst: | ||
|
r6028 | num = inst.code == 404 and errno.ENOENT or None | ||
raise IOError(num, inst) | ||||
|
r28883 | except urlerr.urlerror as inst: | ||
|
r41451 | raise IOError(None, inst.reason) | ||
|
r1101 | |||
|
r8612 | if code == 200: | ||
# HTTPRangeHandler does nothing if remote does not support | ||||
# Range headers and returns the full entity. Let's slice it. | ||||
if bytes: | ||||
data = data[self.pos:self.pos + bytes] | ||||
else: | ||||
data = data[self.pos:] | ||||
elif bytes: | ||||
|
r7274 | data = data[:bytes] | ||
|
r8612 | self.pos += len(data) | ||
|
r7274 | return data | ||
|
r20055 | def readlines(self): | ||
return self.read().splitlines(True) | ||||
|
r11066 | def __iter__(self): | ||
|
r20055 | return iter(self.readlines()) | ||
|
r11066 | def close(self): | ||
pass | ||||
|
r7274 | |||
|
r36443 | # _RangeError and _HTTPRangeHandler were originally in byterange.py, | ||
# which was itself extracted from urlgrabber. See the last version of | ||||
# byterange.py from history if you need more information. | ||||
class _RangeError(IOError): | ||||
"""Error raised when an unsatisfiable range is requested.""" | ||||
class _HTTPRangeHandler(urlreq.basehandler): | ||||
"""Handler that enables HTTP Range headers. | ||||
This was extremely simple. The Range header is a HTTP feature to | ||||
begin with so all this class does is tell urllib2 that the | ||||
"206 Partial Content" response from the HTTP server is what we | ||||
expected. | ||||
""" | ||||
def http_error_206(self, req, fp, code, msg, hdrs): | ||||
# 206 Partial Content Response | ||||
r = urlreq.addinfourl(fp, hdrs, req.get_full_url()) | ||||
r.code = code | ||||
r.msg = msg | ||||
return r | ||||
def http_error_416(self, req, fp, code, msg, hdrs): | ||||
# HTTP's Range Not Satisfiable error | ||||
raise _RangeError('Requested Range Not Satisfiable') | ||||
|
r7274 | def build_opener(ui, authinfo): | ||
# urllib cannot handle URLs with embedded user or passwd | ||||
urlopener = url.opener(ui, authinfo) | ||||
|
r36443 | urlopener.add_handler(_HTTPRangeHandler()) | ||
|
r7274 | |||
|
r31241 | class statichttpvfs(vfsmod.abstractvfs): | ||
|
r14091 | def __init__(self, base): | ||
self.base = base | ||||
|
r23552 | def __call__(self, path, mode='r', *args, **kw): | ||
|
r13533 | if mode not in ('r', 'rb'): | ||
|
r11066 | raise IOError('Permission denied') | ||
|
r28883 | f = "/".join((self.base, urlreq.quote(path))) | ||
|
r7274 | return httprangereader(f, urlopener) | ||
|
r17725 | def join(self, path): | ||
if path: | ||||
|
r34944 | return pathutil.join(self.base, path) | ||
|
r17725 | else: | ||
return self.base | ||||
|
r17649 | return statichttpvfs | ||
|
r1101 | |||
|
r17192 | class statichttppeer(localrepo.localpeer): | ||
def local(self): | ||||
return None | ||||
|
r17193 | def canpush(self): | ||
return False | ||||
|
r17192 | |||
|
r39800 | class statichttprepository(localrepo.localrepository, | ||
localrepo.revlogfilestorage): | ||||
|
r19778 | supported = localrepo.localrepository._basesupported | ||
|
r1101 | def __init__(self, ui, path): | ||
|
r2673 | self._url = path | ||
|
r1101 | self.ui = ui | ||
|
r3853 | |||
|
r11066 | self.root = path | ||
|
r14076 | u = util.url(path.rstrip('/') + "/.hg") | ||
|
r13819 | self.path, authinfo = u.authinfo() | ||
|
r7274 | |||
|
r31147 | vfsclass = build_opener(ui, authinfo) | ||
self.vfs = vfsclass(self.path) | ||||
|
r33533 | self.cachevfs = vfsclass(self.vfs.join('cache')) | ||
|
r15922 | self._phasedefaults = [] | ||
|
r6028 | |||
|
r23561 | self.names = namespaces.namespaces() | ||
|
r32730 | self.filtername = None | ||
|
r23558 | |||
|
r3851 | try: | ||
|
r39730 | requirements = set(self.vfs.read(b'requires').splitlines()) | ||
|
r25660 | except IOError as inst: | ||
|
r7178 | if inst.errno != errno.ENOENT: | ||
raise | ||||
|
r14482 | requirements = set() | ||
|
r7178 | # check if it is a non-empty old-style repository | ||
try: | ||||
|
r23877 | fp = self.vfs("00changelog.i") | ||
|
r13400 | fp.read(1) | ||
fp.close() | ||||
|
r25660 | except IOError as inst: | ||
|
r7178 | if inst.errno != errno.ENOENT: | ||
raise | ||||
# we do not care about empty old-style repositories here | ||||
|
r6028 | msg = _("'%s' does not appear to be an hg repository") % path | ||
|
r7637 | raise error.RepoError(msg) | ||
|
r3851 | |||
|
r39730 | supportedrequirements = localrepo.gathersupportedrequirements(ui) | ||
localrepo.ensurerequirementsrecognized(requirements, | ||||
supportedrequirements) | ||||
|
r39731 | localrepo.ensurerequirementscompatible(ui, requirements) | ||
|
r39730 | |||
|
r3851 | # setup store | ||
|
r39734 | self.store = localrepo.makestore(requirements, self.path, vfsclass) | ||
|
r6897 | self.spath = self.store.path | ||
|
r23878 | self.svfs = self.store.opener | ||
|
r6897 | self.sjoin = self.store.join | ||
|
r16115 | self._filecache = {} | ||
|
r17192 | self.requirements = requirements | ||
|
r3851 | |||
|
r39799 | rootmanifest = manifest.manifestrevlog(self.svfs) | ||
|
r41067 | self.manifestlog = manifest.manifestlog(self.svfs, self, rootmanifest, | ||
self.narrowmatch()) | ||||
|
r23878 | self.changelog = changelog.changelog(self.svfs) | ||
|
r9146 | self._tags = None | ||
|
r1101 | self.nodetagscache = None | ||
|
r18189 | self._branchcaches = {} | ||
|
r24373 | self._revbranchcache = None | ||
|
r1598 | self.encodepats = None | ||
self.decodepats = None | ||||
|
r24377 | self._transref = None | ||
|
r17192 | |||
def _restrictcapabilities(self, caps): | ||||
|
r20962 | caps = super(statichttprepository, self)._restrictcapabilities(caps) | ||
|
r17192 | return caps.difference(["pushkey"]) | ||
|
r1101 | |||
|
r2673 | def url(self): | ||
|
r7211 | return self._url | ||
|
r2673 | |||
|
r1101 | def local(self): | ||
return False | ||||
|
r2740 | |||
|
r17192 | def peer(self): | ||
return statichttppeer(self) | ||||
|
r33605 | def wlock(self, wait=True): | ||
|
r36675 | raise error.LockUnavailable(0, _('lock not available'), 'lock', | ||
|
r33605 | _('cannot lock static-http repository')) | ||
|
r7005 | def lock(self, wait=True): | ||
|
r26587 | raise error.Abort(_('cannot lock static-http repository')) | ||
|
r7005 | |||
|
r29738 | def _writecaches(self): | ||
pass # statichttprepository are read only | ||||
|
r39585 | def instance(ui, path, create, intents=None, createopts=None): | ||
|
r2740 | if create: | ||
|
r26587 | raise error.Abort(_('cannot create new static-http repository')) | ||
|
r4853 | return statichttprepository(ui, path[7:]) | ||