##// END OF EJS Templates
statichttp: add the missing `features` attribute...
marmoute -
r47999:d00177d0 default
parent child Browse files
Show More
@@ -1,265 +1,266 b''
1 # statichttprepo.py - simple http repository class for mercurial
1 # statichttprepo.py - simple http repository class for mercurial
2 #
2 #
3 # This provides read-only repo access to repositories exported via static http
3 # This provides read-only repo access to repositories exported via static http
4 #
4 #
5 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
5 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import errno
12 import errno
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import sha1nodeconstants
15 from .node import sha1nodeconstants
16 from . import (
16 from . import (
17 branchmap,
17 branchmap,
18 changelog,
18 changelog,
19 error,
19 error,
20 localrepo,
20 localrepo,
21 manifest,
21 manifest,
22 namespaces,
22 namespaces,
23 pathutil,
23 pathutil,
24 pycompat,
24 pycompat,
25 url,
25 url,
26 util,
26 util,
27 vfs as vfsmod,
27 vfs as vfsmod,
28 )
28 )
29 from .utils import (
29 from .utils import (
30 urlutil,
30 urlutil,
31 )
31 )
32
32
33 urlerr = util.urlerr
33 urlerr = util.urlerr
34 urlreq = util.urlreq
34 urlreq = util.urlreq
35
35
36
36
37 class httprangereader(object):
37 class httprangereader(object):
38 def __init__(self, url, opener):
38 def __init__(self, url, opener):
39 # we assume opener has HTTPRangeHandler
39 # we assume opener has HTTPRangeHandler
40 self.url = url
40 self.url = url
41 self.pos = 0
41 self.pos = 0
42 self.opener = opener
42 self.opener = opener
43 self.name = url
43 self.name = url
44
44
45 def __enter__(self):
45 def __enter__(self):
46 return self
46 return self
47
47
48 def __exit__(self, exc_type, exc_value, traceback):
48 def __exit__(self, exc_type, exc_value, traceback):
49 self.close()
49 self.close()
50
50
51 def seek(self, pos):
51 def seek(self, pos):
52 self.pos = pos
52 self.pos = pos
53
53
54 def read(self, bytes=None):
54 def read(self, bytes=None):
55 req = urlreq.request(pycompat.strurl(self.url))
55 req = urlreq.request(pycompat.strurl(self.url))
56 end = b''
56 end = b''
57 if bytes:
57 if bytes:
58 end = self.pos + bytes - 1
58 end = self.pos + bytes - 1
59 if self.pos or end:
59 if self.pos or end:
60 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
60 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
61
61
62 try:
62 try:
63 f = self.opener.open(req)
63 f = self.opener.open(req)
64 data = f.read()
64 data = f.read()
65 code = f.code
65 code = f.code
66 except urlerr.httperror as inst:
66 except urlerr.httperror as inst:
67 num = inst.code == 404 and errno.ENOENT or None
67 num = inst.code == 404 and errno.ENOENT or None
68 # Explicitly convert the exception to str as Py3 will try
68 # Explicitly convert the exception to str as Py3 will try
69 # convert it to local encoding and with as the HTTPResponse
69 # convert it to local encoding and with as the HTTPResponse
70 # instance doesn't support encode.
70 # instance doesn't support encode.
71 raise IOError(num, str(inst))
71 raise IOError(num, str(inst))
72 except urlerr.urlerror as inst:
72 except urlerr.urlerror as inst:
73 raise IOError(None, inst.reason)
73 raise IOError(None, inst.reason)
74
74
75 if code == 200:
75 if code == 200:
76 # HTTPRangeHandler does nothing if remote does not support
76 # HTTPRangeHandler does nothing if remote does not support
77 # Range headers and returns the full entity. Let's slice it.
77 # Range headers and returns the full entity. Let's slice it.
78 if bytes:
78 if bytes:
79 data = data[self.pos : self.pos + bytes]
79 data = data[self.pos : self.pos + bytes]
80 else:
80 else:
81 data = data[self.pos :]
81 data = data[self.pos :]
82 elif bytes:
82 elif bytes:
83 data = data[:bytes]
83 data = data[:bytes]
84 self.pos += len(data)
84 self.pos += len(data)
85 return data
85 return data
86
86
87 def readlines(self):
87 def readlines(self):
88 return self.read().splitlines(True)
88 return self.read().splitlines(True)
89
89
90 def __iter__(self):
90 def __iter__(self):
91 return iter(self.readlines())
91 return iter(self.readlines())
92
92
93 def close(self):
93 def close(self):
94 pass
94 pass
95
95
96
96
97 # _RangeError and _HTTPRangeHandler were originally in byterange.py,
97 # _RangeError and _HTTPRangeHandler were originally in byterange.py,
98 # which was itself extracted from urlgrabber. See the last version of
98 # which was itself extracted from urlgrabber. See the last version of
99 # byterange.py from history if you need more information.
99 # byterange.py from history if you need more information.
100 class _RangeError(IOError):
100 class _RangeError(IOError):
101 """Error raised when an unsatisfiable range is requested."""
101 """Error raised when an unsatisfiable range is requested."""
102
102
103
103
104 class _HTTPRangeHandler(urlreq.basehandler):
104 class _HTTPRangeHandler(urlreq.basehandler):
105 """Handler that enables HTTP Range headers.
105 """Handler that enables HTTP Range headers.
106
106
107 This was extremely simple. The Range header is a HTTP feature to
107 This was extremely simple. The Range header is a HTTP feature to
108 begin with so all this class does is tell urllib2 that the
108 begin with so all this class does is tell urllib2 that the
109 "206 Partial Content" response from the HTTP server is what we
109 "206 Partial Content" response from the HTTP server is what we
110 expected.
110 expected.
111 """
111 """
112
112
113 def http_error_206(self, req, fp, code, msg, hdrs):
113 def http_error_206(self, req, fp, code, msg, hdrs):
114 # 206 Partial Content Response
114 # 206 Partial Content Response
115 r = urlreq.addinfourl(fp, hdrs, req.get_full_url())
115 r = urlreq.addinfourl(fp, hdrs, req.get_full_url())
116 r.code = code
116 r.code = code
117 r.msg = msg
117 r.msg = msg
118 return r
118 return r
119
119
120 def http_error_416(self, req, fp, code, msg, hdrs):
120 def http_error_416(self, req, fp, code, msg, hdrs):
121 # HTTP's Range Not Satisfiable error
121 # HTTP's Range Not Satisfiable error
122 raise _RangeError(b'Requested Range Not Satisfiable')
122 raise _RangeError(b'Requested Range Not Satisfiable')
123
123
124
124
125 def build_opener(ui, authinfo):
125 def build_opener(ui, authinfo):
126 # urllib cannot handle URLs with embedded user or passwd
126 # urllib cannot handle URLs with embedded user or passwd
127 urlopener = url.opener(ui, authinfo)
127 urlopener = url.opener(ui, authinfo)
128 urlopener.add_handler(_HTTPRangeHandler())
128 urlopener.add_handler(_HTTPRangeHandler())
129
129
130 class statichttpvfs(vfsmod.abstractvfs):
130 class statichttpvfs(vfsmod.abstractvfs):
131 def __init__(self, base):
131 def __init__(self, base):
132 self.base = base
132 self.base = base
133 self.options = {}
133 self.options = {}
134
134
135 def __call__(self, path, mode=b'r', *args, **kw):
135 def __call__(self, path, mode=b'r', *args, **kw):
136 if mode not in (b'r', b'rb'):
136 if mode not in (b'r', b'rb'):
137 raise IOError(b'Permission denied')
137 raise IOError(b'Permission denied')
138 f = b"/".join((self.base, urlreq.quote(path)))
138 f = b"/".join((self.base, urlreq.quote(path)))
139 return httprangereader(f, urlopener)
139 return httprangereader(f, urlopener)
140
140
141 def join(self, path):
141 def join(self, path):
142 if path:
142 if path:
143 return pathutil.join(self.base, path)
143 return pathutil.join(self.base, path)
144 else:
144 else:
145 return self.base
145 return self.base
146
146
147 return statichttpvfs
147 return statichttpvfs
148
148
149
149
150 class statichttppeer(localrepo.localpeer):
150 class statichttppeer(localrepo.localpeer):
151 def local(self):
151 def local(self):
152 return None
152 return None
153
153
154 def canpush(self):
154 def canpush(self):
155 return False
155 return False
156
156
157
157
158 class statichttprepository(
158 class statichttprepository(
159 localrepo.localrepository, localrepo.revlogfilestorage
159 localrepo.localrepository, localrepo.revlogfilestorage
160 ):
160 ):
161 supported = localrepo.localrepository._basesupported
161 supported = localrepo.localrepository._basesupported
162
162
163 def __init__(self, ui, path):
163 def __init__(self, ui, path):
164 self._url = path
164 self._url = path
165 self.ui = ui
165 self.ui = ui
166
166
167 self.root = path
167 self.root = path
168 u = urlutil.url(path.rstrip(b'/') + b"/.hg")
168 u = urlutil.url(path.rstrip(b'/') + b"/.hg")
169 self.path, authinfo = u.authinfo()
169 self.path, authinfo = u.authinfo()
170
170
171 vfsclass = build_opener(ui, authinfo)
171 vfsclass = build_opener(ui, authinfo)
172 self.vfs = vfsclass(self.path)
172 self.vfs = vfsclass(self.path)
173 self.cachevfs = vfsclass(self.vfs.join(b'cache'))
173 self.cachevfs = vfsclass(self.vfs.join(b'cache'))
174 self._phasedefaults = []
174 self._phasedefaults = []
175
175
176 self.names = namespaces.namespaces()
176 self.names = namespaces.namespaces()
177 self.filtername = None
177 self.filtername = None
178 self._extrafilterid = None
178 self._extrafilterid = None
179 self._wanted_sidedata = set()
179 self._wanted_sidedata = set()
180 self.features = set()
180
181
181 try:
182 try:
182 requirements = set(self.vfs.read(b'requires').splitlines())
183 requirements = set(self.vfs.read(b'requires').splitlines())
183 except IOError as inst:
184 except IOError as inst:
184 if inst.errno != errno.ENOENT:
185 if inst.errno != errno.ENOENT:
185 raise
186 raise
186 requirements = set()
187 requirements = set()
187
188
188 # check if it is a non-empty old-style repository
189 # check if it is a non-empty old-style repository
189 try:
190 try:
190 fp = self.vfs(b"00changelog.i")
191 fp = self.vfs(b"00changelog.i")
191 fp.read(1)
192 fp.read(1)
192 fp.close()
193 fp.close()
193 except IOError as inst:
194 except IOError as inst:
194 if inst.errno != errno.ENOENT:
195 if inst.errno != errno.ENOENT:
195 raise
196 raise
196 # we do not care about empty old-style repositories here
197 # we do not care about empty old-style repositories here
197 msg = _(b"'%s' does not appear to be an hg repository") % path
198 msg = _(b"'%s' does not appear to be an hg repository") % path
198 raise error.RepoError(msg)
199 raise error.RepoError(msg)
199
200
200 supportedrequirements = localrepo.gathersupportedrequirements(ui)
201 supportedrequirements = localrepo.gathersupportedrequirements(ui)
201 localrepo.ensurerequirementsrecognized(
202 localrepo.ensurerequirementsrecognized(
202 requirements, supportedrequirements
203 requirements, supportedrequirements
203 )
204 )
204 localrepo.ensurerequirementscompatible(ui, requirements)
205 localrepo.ensurerequirementscompatible(ui, requirements)
205 self.nodeconstants = sha1nodeconstants
206 self.nodeconstants = sha1nodeconstants
206 self.nullid = self.nodeconstants.nullid
207 self.nullid = self.nodeconstants.nullid
207
208
208 # setup store
209 # setup store
209 self.store = localrepo.makestore(requirements, self.path, vfsclass)
210 self.store = localrepo.makestore(requirements, self.path, vfsclass)
210 self.spath = self.store.path
211 self.spath = self.store.path
211 self.svfs = self.store.opener
212 self.svfs = self.store.opener
212 self.sjoin = self.store.join
213 self.sjoin = self.store.join
213 self._filecache = {}
214 self._filecache = {}
214 self.requirements = requirements
215 self.requirements = requirements
215
216
216 rootmanifest = manifest.manifestrevlog(self.nodeconstants, self.svfs)
217 rootmanifest = manifest.manifestrevlog(self.nodeconstants, self.svfs)
217 self.manifestlog = manifest.manifestlog(
218 self.manifestlog = manifest.manifestlog(
218 self.svfs, self, rootmanifest, self.narrowmatch()
219 self.svfs, self, rootmanifest, self.narrowmatch()
219 )
220 )
220 self.changelog = changelog.changelog(self.svfs)
221 self.changelog = changelog.changelog(self.svfs)
221 self._tags = None
222 self._tags = None
222 self.nodetagscache = None
223 self.nodetagscache = None
223 self._branchcaches = branchmap.BranchMapCache()
224 self._branchcaches = branchmap.BranchMapCache()
224 self._revbranchcache = None
225 self._revbranchcache = None
225 self.encodepats = None
226 self.encodepats = None
226 self.decodepats = None
227 self.decodepats = None
227 self._transref = None
228 self._transref = None
228
229
229 def _restrictcapabilities(self, caps):
230 def _restrictcapabilities(self, caps):
230 caps = super(statichttprepository, self)._restrictcapabilities(caps)
231 caps = super(statichttprepository, self)._restrictcapabilities(caps)
231 return caps.difference([b"pushkey"])
232 return caps.difference([b"pushkey"])
232
233
233 def url(self):
234 def url(self):
234 return self._url
235 return self._url
235
236
236 def local(self):
237 def local(self):
237 return False
238 return False
238
239
239 def peer(self):
240 def peer(self):
240 return statichttppeer(self)
241 return statichttppeer(self)
241
242
242 def wlock(self, wait=True):
243 def wlock(self, wait=True):
243 raise error.LockUnavailable(
244 raise error.LockUnavailable(
244 0,
245 0,
245 _(b'lock not available'),
246 _(b'lock not available'),
246 b'lock',
247 b'lock',
247 _(b'cannot lock static-http repository'),
248 _(b'cannot lock static-http repository'),
248 )
249 )
249
250
250 def lock(self, wait=True):
251 def lock(self, wait=True):
251 raise error.LockUnavailable(
252 raise error.LockUnavailable(
252 0,
253 0,
253 _(b'lock not available'),
254 _(b'lock not available'),
254 b'lock',
255 b'lock',
255 _(b'cannot lock static-http repository'),
256 _(b'cannot lock static-http repository'),
256 )
257 )
257
258
258 def _writecaches(self):
259 def _writecaches(self):
259 pass # statichttprepository are read only
260 pass # statichttprepository are read only
260
261
261
262
262 def instance(ui, path, create, intents=None, createopts=None):
263 def instance(ui, path, create, intents=None, createopts=None):
263 if create:
264 if create:
264 raise error.Abort(_(b'cannot create new static-http repository'))
265 raise error.Abort(_(b'cannot create new static-http repository'))
265 return statichttprepository(ui, path[7:])
266 return statichttprepository(ui, path[7:])
General Comments 0
You need to be logged in to leave comments. Login now