##// END OF EJS Templates
httppeer: unify hint message for PeerTransportError...
FUJIWARA Katsunori -
r32087:b59a292d stable
parent child Browse files
Show More
@@ -1,402 +1,402 b''
1 # httppeer.py - HTTP repository proxy classes for mercurial
1 # httppeer.py - HTTP repository proxy classes for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import errno
11 import errno
12 import os
12 import os
13 import socket
13 import socket
14 import struct
14 import struct
15 import tempfile
15 import tempfile
16
16
17 from .i18n import _
17 from .i18n import _
18 from .node import nullid
18 from .node import nullid
19 from . import (
19 from . import (
20 bundle2,
20 bundle2,
21 error,
21 error,
22 httpconnection,
22 httpconnection,
23 pycompat,
23 pycompat,
24 statichttprepo,
24 statichttprepo,
25 url,
25 url,
26 util,
26 util,
27 wireproto,
27 wireproto,
28 )
28 )
29
29
30 httplib = util.httplib
30 httplib = util.httplib
31 urlerr = util.urlerr
31 urlerr = util.urlerr
32 urlreq = util.urlreq
32 urlreq = util.urlreq
33
33
34 def encodevalueinheaders(value, header, limit):
34 def encodevalueinheaders(value, header, limit):
35 """Encode a string value into multiple HTTP headers.
35 """Encode a string value into multiple HTTP headers.
36
36
37 ``value`` will be encoded into 1 or more HTTP headers with the names
37 ``value`` will be encoded into 1 or more HTTP headers with the names
38 ``header-<N>`` where ``<N>`` is an integer starting at 1. Each header
38 ``header-<N>`` where ``<N>`` is an integer starting at 1. Each header
39 name + value will be at most ``limit`` bytes long.
39 name + value will be at most ``limit`` bytes long.
40
40
41 Returns an iterable of 2-tuples consisting of header names and values.
41 Returns an iterable of 2-tuples consisting of header names and values.
42 """
42 """
43 fmt = header + '-%s'
43 fmt = header + '-%s'
44 valuelen = limit - len(fmt % '000') - len(': \r\n')
44 valuelen = limit - len(fmt % '000') - len(': \r\n')
45 result = []
45 result = []
46
46
47 n = 0
47 n = 0
48 for i in xrange(0, len(value), valuelen):
48 for i in xrange(0, len(value), valuelen):
49 n += 1
49 n += 1
50 result.append((fmt % str(n), value[i:i + valuelen]))
50 result.append((fmt % str(n), value[i:i + valuelen]))
51
51
52 return result
52 return result
53
53
54 def _wraphttpresponse(resp):
54 def _wraphttpresponse(resp):
55 """Wrap an HTTPResponse with common error handlers.
55 """Wrap an HTTPResponse with common error handlers.
56
56
57 This ensures that any I/O from any consumer raises the appropriate
57 This ensures that any I/O from any consumer raises the appropriate
58 error and messaging.
58 error and messaging.
59 """
59 """
60 origread = resp.read
60 origread = resp.read
61
61
62 class readerproxy(resp.__class__):
62 class readerproxy(resp.__class__):
63 def read(self, size=None):
63 def read(self, size=None):
64 try:
64 try:
65 return origread(size)
65 return origread(size)
66 except httplib.IncompleteRead as e:
66 except httplib.IncompleteRead as e:
67 # e.expected is an integer if length known or None otherwise.
67 # e.expected is an integer if length known or None otherwise.
68 if e.expected:
68 if e.expected:
69 msg = _('HTTP request error (incomplete response; '
69 msg = _('HTTP request error (incomplete response; '
70 'expected %d bytes got %d)') % (e.expected,
70 'expected %d bytes got %d)') % (e.expected,
71 len(e.partial))
71 len(e.partial))
72 else:
72 else:
73 msg = _('HTTP request error (incomplete response)')
73 msg = _('HTTP request error (incomplete response)')
74
74
75 raise error.PeerTransportError(
75 raise error.PeerTransportError(
76 msg,
76 msg,
77 hint=_('this may be an intermittent network failure; '
77 hint=_('this may be an intermittent network failure; '
78 'if the error persists, consider contacting the '
78 'if the error persists, consider contacting the '
79 'network or server operator'))
79 'network or server operator'))
80 except httplib.HTTPException as e:
80 except httplib.HTTPException as e:
81 raise error.PeerTransportError(
81 raise error.PeerTransportError(
82 _('HTTP request error (%s)') % e,
82 _('HTTP request error (%s)') % e,
83 hint=_('this may be an intermittent failure; '
83 hint=_('this may be an intermittent network failure; '
84 'if the error persists, consider contacting the '
84 'if the error persists, consider contacting the '
85 'network or server operator'))
85 'network or server operator'))
86
86
87 resp.__class__ = readerproxy
87 resp.__class__ = readerproxy
88
88
89 class httppeer(wireproto.wirepeer):
89 class httppeer(wireproto.wirepeer):
90 def __init__(self, ui, path):
90 def __init__(self, ui, path):
91 self.path = path
91 self.path = path
92 self.caps = None
92 self.caps = None
93 self.handler = None
93 self.handler = None
94 self.urlopener = None
94 self.urlopener = None
95 self.requestbuilder = None
95 self.requestbuilder = None
96 u = util.url(path)
96 u = util.url(path)
97 if u.query or u.fragment:
97 if u.query or u.fragment:
98 raise error.Abort(_('unsupported URL component: "%s"') %
98 raise error.Abort(_('unsupported URL component: "%s"') %
99 (u.query or u.fragment))
99 (u.query or u.fragment))
100
100
101 # urllib cannot handle URLs with embedded user or passwd
101 # urllib cannot handle URLs with embedded user or passwd
102 self._url, authinfo = u.authinfo()
102 self._url, authinfo = u.authinfo()
103
103
104 self.ui = ui
104 self.ui = ui
105 self.ui.debug('using %s\n' % self._url)
105 self.ui.debug('using %s\n' % self._url)
106
106
107 self.urlopener = url.opener(ui, authinfo)
107 self.urlopener = url.opener(ui, authinfo)
108 self.requestbuilder = urlreq.request
108 self.requestbuilder = urlreq.request
109
109
110 def __del__(self):
110 def __del__(self):
111 urlopener = getattr(self, 'urlopener', None)
111 urlopener = getattr(self, 'urlopener', None)
112 if urlopener:
112 if urlopener:
113 for h in urlopener.handlers:
113 for h in urlopener.handlers:
114 h.close()
114 h.close()
115 getattr(h, "close_all", lambda : None)()
115 getattr(h, "close_all", lambda : None)()
116
116
117 def url(self):
117 def url(self):
118 return self.path
118 return self.path
119
119
120 # look up capabilities only when needed
120 # look up capabilities only when needed
121
121
122 def _fetchcaps(self):
122 def _fetchcaps(self):
123 self.caps = set(self._call('capabilities').split())
123 self.caps = set(self._call('capabilities').split())
124
124
125 def _capabilities(self):
125 def _capabilities(self):
126 if self.caps is None:
126 if self.caps is None:
127 try:
127 try:
128 self._fetchcaps()
128 self._fetchcaps()
129 except error.RepoError:
129 except error.RepoError:
130 self.caps = set()
130 self.caps = set()
131 self.ui.debug('capabilities: %s\n' %
131 self.ui.debug('capabilities: %s\n' %
132 (' '.join(self.caps or ['none'])))
132 (' '.join(self.caps or ['none'])))
133 return self.caps
133 return self.caps
134
134
135 def lock(self):
135 def lock(self):
136 raise error.Abort(_('operation not supported over http'))
136 raise error.Abort(_('operation not supported over http'))
137
137
138 def _callstream(self, cmd, _compressible=False, **args):
138 def _callstream(self, cmd, _compressible=False, **args):
139 if cmd == 'pushkey':
139 if cmd == 'pushkey':
140 args['data'] = ''
140 args['data'] = ''
141 data = args.pop('data', None)
141 data = args.pop('data', None)
142 headers = args.pop('headers', {})
142 headers = args.pop('headers', {})
143
143
144 self.ui.debug("sending %s command\n" % cmd)
144 self.ui.debug("sending %s command\n" % cmd)
145 q = [('cmd', cmd)]
145 q = [('cmd', cmd)]
146 headersize = 0
146 headersize = 0
147 varyheaders = []
147 varyheaders = []
148 # Important: don't use self.capable() here or else you end up
148 # Important: don't use self.capable() here or else you end up
149 # with infinite recursion when trying to look up capabilities
149 # with infinite recursion when trying to look up capabilities
150 # for the first time.
150 # for the first time.
151 postargsok = self.caps is not None and 'httppostargs' in self.caps
151 postargsok = self.caps is not None and 'httppostargs' in self.caps
152 # TODO: support for httppostargs when data is a file-like
152 # TODO: support for httppostargs when data is a file-like
153 # object rather than a basestring
153 # object rather than a basestring
154 canmungedata = not data or isinstance(data, basestring)
154 canmungedata = not data or isinstance(data, basestring)
155 if postargsok and canmungedata:
155 if postargsok and canmungedata:
156 strargs = urlreq.urlencode(sorted(args.items()))
156 strargs = urlreq.urlencode(sorted(args.items()))
157 if strargs:
157 if strargs:
158 if not data:
158 if not data:
159 data = strargs
159 data = strargs
160 elif isinstance(data, basestring):
160 elif isinstance(data, basestring):
161 data = strargs + data
161 data = strargs + data
162 headers['X-HgArgs-Post'] = len(strargs)
162 headers['X-HgArgs-Post'] = len(strargs)
163 else:
163 else:
164 if len(args) > 0:
164 if len(args) > 0:
165 httpheader = self.capable('httpheader')
165 httpheader = self.capable('httpheader')
166 if httpheader:
166 if httpheader:
167 headersize = int(httpheader.split(',', 1)[0])
167 headersize = int(httpheader.split(',', 1)[0])
168 if headersize > 0:
168 if headersize > 0:
169 # The headers can typically carry more data than the URL.
169 # The headers can typically carry more data than the URL.
170 encargs = urlreq.urlencode(sorted(args.items()))
170 encargs = urlreq.urlencode(sorted(args.items()))
171 for header, value in encodevalueinheaders(encargs, 'X-HgArg',
171 for header, value in encodevalueinheaders(encargs, 'X-HgArg',
172 headersize):
172 headersize):
173 headers[header] = value
173 headers[header] = value
174 varyheaders.append(header)
174 varyheaders.append(header)
175 else:
175 else:
176 q += sorted(args.items())
176 q += sorted(args.items())
177 qs = '?%s' % urlreq.urlencode(q)
177 qs = '?%s' % urlreq.urlencode(q)
178 cu = "%s%s" % (self._url, qs)
178 cu = "%s%s" % (self._url, qs)
179 size = 0
179 size = 0
180 if util.safehasattr(data, 'length'):
180 if util.safehasattr(data, 'length'):
181 size = data.length
181 size = data.length
182 elif data is not None:
182 elif data is not None:
183 size = len(data)
183 size = len(data)
184 if size and self.ui.configbool('ui', 'usehttp2', False):
184 if size and self.ui.configbool('ui', 'usehttp2', False):
185 headers['Expect'] = '100-Continue'
185 headers['Expect'] = '100-Continue'
186 headers['X-HgHttp2'] = '1'
186 headers['X-HgHttp2'] = '1'
187 if data is not None and 'Content-Type' not in headers:
187 if data is not None and 'Content-Type' not in headers:
188 headers['Content-Type'] = 'application/mercurial-0.1'
188 headers['Content-Type'] = 'application/mercurial-0.1'
189
189
190 # Tell the server we accept application/mercurial-0.2 and multiple
190 # Tell the server we accept application/mercurial-0.2 and multiple
191 # compression formats if the server is capable of emitting those
191 # compression formats if the server is capable of emitting those
192 # payloads.
192 # payloads.
193 protoparams = []
193 protoparams = []
194
194
195 mediatypes = set()
195 mediatypes = set()
196 if self.caps is not None:
196 if self.caps is not None:
197 mt = self.capable('httpmediatype')
197 mt = self.capable('httpmediatype')
198 if mt:
198 if mt:
199 protoparams.append('0.1')
199 protoparams.append('0.1')
200 mediatypes = set(mt.split(','))
200 mediatypes = set(mt.split(','))
201
201
202 if '0.2tx' in mediatypes:
202 if '0.2tx' in mediatypes:
203 protoparams.append('0.2')
203 protoparams.append('0.2')
204
204
205 if '0.2tx' in mediatypes and self.capable('compression'):
205 if '0.2tx' in mediatypes and self.capable('compression'):
206 # We /could/ compare supported compression formats and prune
206 # We /could/ compare supported compression formats and prune
207 # non-mutually supported or error if nothing is mutually supported.
207 # non-mutually supported or error if nothing is mutually supported.
208 # For now, send the full list to the server and have it error.
208 # For now, send the full list to the server and have it error.
209 comps = [e.wireprotosupport().name for e in
209 comps = [e.wireprotosupport().name for e in
210 util.compengines.supportedwireengines(util.CLIENTROLE)]
210 util.compengines.supportedwireengines(util.CLIENTROLE)]
211 protoparams.append('comp=%s' % ','.join(comps))
211 protoparams.append('comp=%s' % ','.join(comps))
212
212
213 if protoparams:
213 if protoparams:
214 protoheaders = encodevalueinheaders(' '.join(protoparams),
214 protoheaders = encodevalueinheaders(' '.join(protoparams),
215 'X-HgProto',
215 'X-HgProto',
216 headersize or 1024)
216 headersize or 1024)
217 for header, value in protoheaders:
217 for header, value in protoheaders:
218 headers[header] = value
218 headers[header] = value
219 varyheaders.append(header)
219 varyheaders.append(header)
220
220
221 if varyheaders:
221 if varyheaders:
222 headers['Vary'] = ','.join(varyheaders)
222 headers['Vary'] = ','.join(varyheaders)
223
223
224 req = self.requestbuilder(cu, data, headers)
224 req = self.requestbuilder(cu, data, headers)
225
225
226 if data is not None:
226 if data is not None:
227 self.ui.debug("sending %s bytes\n" % size)
227 self.ui.debug("sending %s bytes\n" % size)
228 req.add_unredirected_header('Content-Length', '%d' % size)
228 req.add_unredirected_header('Content-Length', '%d' % size)
229 try:
229 try:
230 resp = self.urlopener.open(req)
230 resp = self.urlopener.open(req)
231 except urlerr.httperror as inst:
231 except urlerr.httperror as inst:
232 if inst.code == 401:
232 if inst.code == 401:
233 raise error.Abort(_('authorization failed'))
233 raise error.Abort(_('authorization failed'))
234 raise
234 raise
235 except httplib.HTTPException as inst:
235 except httplib.HTTPException as inst:
236 self.ui.debug('http error while sending %s command\n' % cmd)
236 self.ui.debug('http error while sending %s command\n' % cmd)
237 self.ui.traceback()
237 self.ui.traceback()
238 raise IOError(None, inst)
238 raise IOError(None, inst)
239
239
240 # Insert error handlers for common I/O failures.
240 # Insert error handlers for common I/O failures.
241 _wraphttpresponse(resp)
241 _wraphttpresponse(resp)
242
242
243 # record the url we got redirected to
243 # record the url we got redirected to
244 resp_url = resp.geturl()
244 resp_url = resp.geturl()
245 if resp_url.endswith(qs):
245 if resp_url.endswith(qs):
246 resp_url = resp_url[:-len(qs)]
246 resp_url = resp_url[:-len(qs)]
247 if self._url.rstrip('/') != resp_url.rstrip('/'):
247 if self._url.rstrip('/') != resp_url.rstrip('/'):
248 if not self.ui.quiet:
248 if not self.ui.quiet:
249 self.ui.warn(_('real URL is %s\n') % resp_url)
249 self.ui.warn(_('real URL is %s\n') % resp_url)
250 self._url = resp_url
250 self._url = resp_url
251 try:
251 try:
252 proto = resp.getheader('content-type')
252 proto = resp.getheader('content-type')
253 except AttributeError:
253 except AttributeError:
254 proto = resp.headers.get('content-type', '')
254 proto = resp.headers.get('content-type', '')
255
255
256 safeurl = util.hidepassword(self._url)
256 safeurl = util.hidepassword(self._url)
257 if proto.startswith('application/hg-error'):
257 if proto.startswith('application/hg-error'):
258 raise error.OutOfBandError(resp.read())
258 raise error.OutOfBandError(resp.read())
259 # accept old "text/plain" and "application/hg-changegroup" for now
259 # accept old "text/plain" and "application/hg-changegroup" for now
260 if not (proto.startswith('application/mercurial-') or
260 if not (proto.startswith('application/mercurial-') or
261 (proto.startswith('text/plain')
261 (proto.startswith('text/plain')
262 and not resp.headers.get('content-length')) or
262 and not resp.headers.get('content-length')) or
263 proto.startswith('application/hg-changegroup')):
263 proto.startswith('application/hg-changegroup')):
264 self.ui.debug("requested URL: '%s'\n" % util.hidepassword(cu))
264 self.ui.debug("requested URL: '%s'\n" % util.hidepassword(cu))
265 raise error.RepoError(
265 raise error.RepoError(
266 _("'%s' does not appear to be an hg repository:\n"
266 _("'%s' does not appear to be an hg repository:\n"
267 "---%%<--- (%s)\n%s\n---%%<---\n")
267 "---%%<--- (%s)\n%s\n---%%<---\n")
268 % (safeurl, proto or 'no content-type', resp.read(1024)))
268 % (safeurl, proto or 'no content-type', resp.read(1024)))
269
269
270 if proto.startswith('application/mercurial-'):
270 if proto.startswith('application/mercurial-'):
271 try:
271 try:
272 version = proto.split('-', 1)[1]
272 version = proto.split('-', 1)[1]
273 version_info = tuple([int(n) for n in version.split('.')])
273 version_info = tuple([int(n) for n in version.split('.')])
274 except ValueError:
274 except ValueError:
275 raise error.RepoError(_("'%s' sent a broken Content-Type "
275 raise error.RepoError(_("'%s' sent a broken Content-Type "
276 "header (%s)") % (safeurl, proto))
276 "header (%s)") % (safeurl, proto))
277
277
278 # TODO consider switching to a decompression reader that uses
278 # TODO consider switching to a decompression reader that uses
279 # generators.
279 # generators.
280 if version_info == (0, 1):
280 if version_info == (0, 1):
281 if _compressible:
281 if _compressible:
282 return util.compengines['zlib'].decompressorreader(resp)
282 return util.compengines['zlib'].decompressorreader(resp)
283 return resp
283 return resp
284 elif version_info == (0, 2):
284 elif version_info == (0, 2):
285 # application/mercurial-0.2 always identifies the compression
285 # application/mercurial-0.2 always identifies the compression
286 # engine in the payload header.
286 # engine in the payload header.
287 elen = struct.unpack('B', resp.read(1))[0]
287 elen = struct.unpack('B', resp.read(1))[0]
288 ename = resp.read(elen)
288 ename = resp.read(elen)
289 engine = util.compengines.forwiretype(ename)
289 engine = util.compengines.forwiretype(ename)
290 return engine.decompressorreader(resp)
290 return engine.decompressorreader(resp)
291 else:
291 else:
292 raise error.RepoError(_("'%s' uses newer protocol %s") %
292 raise error.RepoError(_("'%s' uses newer protocol %s") %
293 (safeurl, version))
293 (safeurl, version))
294
294
295 if _compressible:
295 if _compressible:
296 return util.compengines['zlib'].decompressorreader(resp)
296 return util.compengines['zlib'].decompressorreader(resp)
297
297
298 return resp
298 return resp
299
299
300 def _call(self, cmd, **args):
300 def _call(self, cmd, **args):
301 fp = self._callstream(cmd, **args)
301 fp = self._callstream(cmd, **args)
302 try:
302 try:
303 return fp.read()
303 return fp.read()
304 finally:
304 finally:
305 # if using keepalive, allow connection to be reused
305 # if using keepalive, allow connection to be reused
306 fp.close()
306 fp.close()
307
307
308 def _callpush(self, cmd, cg, **args):
308 def _callpush(self, cmd, cg, **args):
309 # have to stream bundle to a temp file because we do not have
309 # have to stream bundle to a temp file because we do not have
310 # http 1.1 chunked transfer.
310 # http 1.1 chunked transfer.
311
311
312 types = self.capable('unbundle')
312 types = self.capable('unbundle')
313 try:
313 try:
314 types = types.split(',')
314 types = types.split(',')
315 except AttributeError:
315 except AttributeError:
316 # servers older than d1b16a746db6 will send 'unbundle' as a
316 # servers older than d1b16a746db6 will send 'unbundle' as a
317 # boolean capability. They only support headerless/uncompressed
317 # boolean capability. They only support headerless/uncompressed
318 # bundles.
318 # bundles.
319 types = [""]
319 types = [""]
320 for x in types:
320 for x in types:
321 if x in bundle2.bundletypes:
321 if x in bundle2.bundletypes:
322 type = x
322 type = x
323 break
323 break
324
324
325 tempname = bundle2.writebundle(self.ui, cg, None, type)
325 tempname = bundle2.writebundle(self.ui, cg, None, type)
326 fp = httpconnection.httpsendfile(self.ui, tempname, "rb")
326 fp = httpconnection.httpsendfile(self.ui, tempname, "rb")
327 headers = {'Content-Type': 'application/mercurial-0.1'}
327 headers = {'Content-Type': 'application/mercurial-0.1'}
328
328
329 try:
329 try:
330 r = self._call(cmd, data=fp, headers=headers, **args)
330 r = self._call(cmd, data=fp, headers=headers, **args)
331 vals = r.split('\n', 1)
331 vals = r.split('\n', 1)
332 if len(vals) < 2:
332 if len(vals) < 2:
333 raise error.ResponseError(_("unexpected response:"), r)
333 raise error.ResponseError(_("unexpected response:"), r)
334 return vals
334 return vals
335 except socket.error as err:
335 except socket.error as err:
336 if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
336 if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
337 raise error.Abort(_('push failed: %s') % err.args[1])
337 raise error.Abort(_('push failed: %s') % err.args[1])
338 raise error.Abort(err.args[1])
338 raise error.Abort(err.args[1])
339 finally:
339 finally:
340 fp.close()
340 fp.close()
341 os.unlink(tempname)
341 os.unlink(tempname)
342
342
343 def _calltwowaystream(self, cmd, fp, **args):
343 def _calltwowaystream(self, cmd, fp, **args):
344 fh = None
344 fh = None
345 fp_ = None
345 fp_ = None
346 filename = None
346 filename = None
347 try:
347 try:
348 # dump bundle to disk
348 # dump bundle to disk
349 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
349 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
350 fh = os.fdopen(fd, pycompat.sysstr("wb"))
350 fh = os.fdopen(fd, pycompat.sysstr("wb"))
351 d = fp.read(4096)
351 d = fp.read(4096)
352 while d:
352 while d:
353 fh.write(d)
353 fh.write(d)
354 d = fp.read(4096)
354 d = fp.read(4096)
355 fh.close()
355 fh.close()
356 # start http push
356 # start http push
357 fp_ = httpconnection.httpsendfile(self.ui, filename, "rb")
357 fp_ = httpconnection.httpsendfile(self.ui, filename, "rb")
358 headers = {'Content-Type': 'application/mercurial-0.1'}
358 headers = {'Content-Type': 'application/mercurial-0.1'}
359 return self._callstream(cmd, data=fp_, headers=headers, **args)
359 return self._callstream(cmd, data=fp_, headers=headers, **args)
360 finally:
360 finally:
361 if fp_ is not None:
361 if fp_ is not None:
362 fp_.close()
362 fp_.close()
363 if fh is not None:
363 if fh is not None:
364 fh.close()
364 fh.close()
365 os.unlink(filename)
365 os.unlink(filename)
366
366
367 def _callcompressable(self, cmd, **args):
367 def _callcompressable(self, cmd, **args):
368 return self._callstream(cmd, _compressible=True, **args)
368 return self._callstream(cmd, _compressible=True, **args)
369
369
370 def _abort(self, exception):
370 def _abort(self, exception):
371 raise exception
371 raise exception
372
372
373 class httpspeer(httppeer):
373 class httpspeer(httppeer):
374 def __init__(self, ui, path):
374 def __init__(self, ui, path):
375 if not url.has_https:
375 if not url.has_https:
376 raise error.Abort(_('Python support for SSL and HTTPS '
376 raise error.Abort(_('Python support for SSL and HTTPS '
377 'is not installed'))
377 'is not installed'))
378 httppeer.__init__(self, ui, path)
378 httppeer.__init__(self, ui, path)
379
379
380 def instance(ui, path, create):
380 def instance(ui, path, create):
381 if create:
381 if create:
382 raise error.Abort(_('cannot create new http repository'))
382 raise error.Abort(_('cannot create new http repository'))
383 try:
383 try:
384 if path.startswith('https:'):
384 if path.startswith('https:'):
385 inst = httpspeer(ui, path)
385 inst = httpspeer(ui, path)
386 else:
386 else:
387 inst = httppeer(ui, path)
387 inst = httppeer(ui, path)
388 try:
388 try:
389 # Try to do useful work when checking compatibility.
389 # Try to do useful work when checking compatibility.
390 # Usually saves a roundtrip since we want the caps anyway.
390 # Usually saves a roundtrip since we want the caps anyway.
391 inst._fetchcaps()
391 inst._fetchcaps()
392 except error.RepoError:
392 except error.RepoError:
393 # No luck, try older compatibility check.
393 # No luck, try older compatibility check.
394 inst.between([(nullid, nullid)])
394 inst.between([(nullid, nullid)])
395 return inst
395 return inst
396 except error.RepoError as httpexception:
396 except error.RepoError as httpexception:
397 try:
397 try:
398 r = statichttprepo.instance(ui, "static-" + path, create)
398 r = statichttprepo.instance(ui, "static-" + path, create)
399 ui.note(_('(falling back to static-http)\n'))
399 ui.note(_('(falling back to static-http)\n'))
400 return r
400 return r
401 except error.RepoError:
401 except error.RepoError:
402 raise httpexception # use the original http RepoError instead
402 raise httpexception # use the original http RepoError instead
General Comments 0
You need to be logged in to leave comments. Login now