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