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