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