##// END OF EJS Templates
httprepo: ignore environment proxies when proxies are disabled
Sebastian Hauer -
r5476:b3afa672 default
parent child Browse files
Show More
@@ -1,455 +1,458 b''
1 # httprepo.py - HTTP repository proxy classes for mercurial
1 # httprepo.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
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 from node import *
9 from node import *
10 from remoterepo import *
10 from remoterepo import *
11 from i18n import _
11 from i18n import _
12 import repo, os, urllib, urllib2, urlparse, zlib, util, httplib
12 import repo, os, urllib, urllib2, urlparse, zlib, util, httplib
13 import errno, keepalive, tempfile, socket, changegroup
13 import errno, keepalive, tempfile, socket, changegroup
14
14
15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
16 def __init__(self, ui):
16 def __init__(self, ui):
17 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
17 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
18 self.ui = ui
18 self.ui = ui
19
19
20 def find_user_password(self, realm, authuri):
20 def find_user_password(self, realm, authuri):
21 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
21 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
22 self, realm, authuri)
22 self, realm, authuri)
23 user, passwd = authinfo
23 user, passwd = authinfo
24 if user and passwd:
24 if user and passwd:
25 return (user, passwd)
25 return (user, passwd)
26
26
27 if not self.ui.interactive:
27 if not self.ui.interactive:
28 raise util.Abort(_('http authorization required'))
28 raise util.Abort(_('http authorization required'))
29
29
30 self.ui.write(_("http authorization required\n"))
30 self.ui.write(_("http authorization required\n"))
31 self.ui.status(_("realm: %s\n") % realm)
31 self.ui.status(_("realm: %s\n") % realm)
32 if user:
32 if user:
33 self.ui.status(_("user: %s\n") % user)
33 self.ui.status(_("user: %s\n") % user)
34 else:
34 else:
35 user = self.ui.prompt(_("user:"), default=None)
35 user = self.ui.prompt(_("user:"), default=None)
36
36
37 if not passwd:
37 if not passwd:
38 passwd = self.ui.getpass()
38 passwd = self.ui.getpass()
39
39
40 self.add_password(realm, authuri, user, passwd)
40 self.add_password(realm, authuri, user, passwd)
41 return (user, passwd)
41 return (user, passwd)
42
42
43 def netlocsplit(netloc):
43 def netlocsplit(netloc):
44 '''split [user[:passwd]@]host[:port] into 4-tuple.'''
44 '''split [user[:passwd]@]host[:port] into 4-tuple.'''
45
45
46 a = netloc.find('@')
46 a = netloc.find('@')
47 if a == -1:
47 if a == -1:
48 user, passwd = None, None
48 user, passwd = None, None
49 else:
49 else:
50 userpass, netloc = netloc[:a], netloc[a+1:]
50 userpass, netloc = netloc[:a], netloc[a+1:]
51 c = userpass.find(':')
51 c = userpass.find(':')
52 if c == -1:
52 if c == -1:
53 user, passwd = urllib.unquote(userpass), None
53 user, passwd = urllib.unquote(userpass), None
54 else:
54 else:
55 user = urllib.unquote(userpass[:c])
55 user = urllib.unquote(userpass[:c])
56 passwd = urllib.unquote(userpass[c+1:])
56 passwd = urllib.unquote(userpass[c+1:])
57 c = netloc.find(':')
57 c = netloc.find(':')
58 if c == -1:
58 if c == -1:
59 host, port = netloc, None
59 host, port = netloc, None
60 else:
60 else:
61 host, port = netloc[:c], netloc[c+1:]
61 host, port = netloc[:c], netloc[c+1:]
62 return host, port, user, passwd
62 return host, port, user, passwd
63
63
64 def netlocunsplit(host, port, user=None, passwd=None):
64 def netlocunsplit(host, port, user=None, passwd=None):
65 '''turn host, port, user, passwd into [user[:passwd]@]host[:port].'''
65 '''turn host, port, user, passwd into [user[:passwd]@]host[:port].'''
66 if port:
66 if port:
67 hostport = host + ':' + port
67 hostport = host + ':' + port
68 else:
68 else:
69 hostport = host
69 hostport = host
70 if user:
70 if user:
71 if passwd:
71 if passwd:
72 userpass = urllib.quote(user) + ':' + urllib.quote(passwd)
72 userpass = urllib.quote(user) + ':' + urllib.quote(passwd)
73 else:
73 else:
74 userpass = urllib.quote(user)
74 userpass = urllib.quote(user)
75 return userpass + '@' + hostport
75 return userpass + '@' + hostport
76 return hostport
76 return hostport
77
77
78 # work around a bug in Python < 2.4.2
78 # work around a bug in Python < 2.4.2
79 # (it leaves a "\n" at the end of Proxy-authorization headers)
79 # (it leaves a "\n" at the end of Proxy-authorization headers)
80 class request(urllib2.Request):
80 class request(urllib2.Request):
81 def add_header(self, key, val):
81 def add_header(self, key, val):
82 if key.lower() == 'proxy-authorization':
82 if key.lower() == 'proxy-authorization':
83 val = val.strip()
83 val = val.strip()
84 return urllib2.Request.add_header(self, key, val)
84 return urllib2.Request.add_header(self, key, val)
85
85
86 class httpsendfile(file):
86 class httpsendfile(file):
87 def __len__(self):
87 def __len__(self):
88 return os.fstat(self.fileno()).st_size
88 return os.fstat(self.fileno()).st_size
89
89
90 def _gen_sendfile(connection):
90 def _gen_sendfile(connection):
91 def _sendfile(self, data):
91 def _sendfile(self, data):
92 # send a file
92 # send a file
93 if isinstance(data, httpsendfile):
93 if isinstance(data, httpsendfile):
94 # if auth required, some data sent twice, so rewind here
94 # if auth required, some data sent twice, so rewind here
95 data.seek(0)
95 data.seek(0)
96 for chunk in util.filechunkiter(data):
96 for chunk in util.filechunkiter(data):
97 connection.send(self, chunk)
97 connection.send(self, chunk)
98 else:
98 else:
99 connection.send(self, data)
99 connection.send(self, data)
100 return _sendfile
100 return _sendfile
101
101
102 class httpconnection(keepalive.HTTPConnection):
102 class httpconnection(keepalive.HTTPConnection):
103 # must be able to send big bundle as stream.
103 # must be able to send big bundle as stream.
104 send = _gen_sendfile(keepalive.HTTPConnection)
104 send = _gen_sendfile(keepalive.HTTPConnection)
105
105
106 class basehttphandler(keepalive.HTTPHandler):
106 class basehttphandler(keepalive.HTTPHandler):
107 def http_open(self, req):
107 def http_open(self, req):
108 return self.do_open(httpconnection, req)
108 return self.do_open(httpconnection, req)
109
109
110 has_https = hasattr(urllib2, 'HTTPSHandler')
110 has_https = hasattr(urllib2, 'HTTPSHandler')
111 if has_https:
111 if has_https:
112 class httpsconnection(httplib.HTTPSConnection):
112 class httpsconnection(httplib.HTTPSConnection):
113 response_class = keepalive.HTTPResponse
113 response_class = keepalive.HTTPResponse
114 # must be able to send big bundle as stream.
114 # must be able to send big bundle as stream.
115 send = _gen_sendfile(httplib.HTTPSConnection)
115 send = _gen_sendfile(httplib.HTTPSConnection)
116
116
117 class httphandler(basehttphandler, urllib2.HTTPSHandler):
117 class httphandler(basehttphandler, urllib2.HTTPSHandler):
118 def https_open(self, req):
118 def https_open(self, req):
119 return self.do_open(httpsconnection, req)
119 return self.do_open(httpsconnection, req)
120 else:
120 else:
121 class httphandler(basehttphandler):
121 class httphandler(basehttphandler):
122 pass
122 pass
123
123
124 # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
124 # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
125 # it doesn't know about the auth type requested. This can happen if
125 # it doesn't know about the auth type requested. This can happen if
126 # somebody is using BasicAuth and types a bad password.
126 # somebody is using BasicAuth and types a bad password.
127 class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler):
127 class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler):
128 def http_error_auth_reqed(self, auth_header, host, req, headers):
128 def http_error_auth_reqed(self, auth_header, host, req, headers):
129 try:
129 try:
130 return urllib2.HTTPDigestAuthHandler.http_error_auth_reqed(
130 return urllib2.HTTPDigestAuthHandler.http_error_auth_reqed(
131 self, auth_header, host, req, headers)
131 self, auth_header, host, req, headers)
132 except ValueError, inst:
132 except ValueError, inst:
133 arg = inst.args[0]
133 arg = inst.args[0]
134 if arg.startswith("AbstractDigestAuthHandler doesn't know "):
134 if arg.startswith("AbstractDigestAuthHandler doesn't know "):
135 return
135 return
136 raise
136 raise
137
137
138 def zgenerator(f):
138 def zgenerator(f):
139 zd = zlib.decompressobj()
139 zd = zlib.decompressobj()
140 try:
140 try:
141 for chunk in util.filechunkiter(f):
141 for chunk in util.filechunkiter(f):
142 yield zd.decompress(chunk)
142 yield zd.decompress(chunk)
143 except httplib.HTTPException, inst:
143 except httplib.HTTPException, inst:
144 raise IOError(None, _('connection ended unexpectedly'))
144 raise IOError(None, _('connection ended unexpectedly'))
145 yield zd.flush()
145 yield zd.flush()
146
146
147 _safe = ('abcdefghijklmnopqrstuvwxyz'
147 _safe = ('abcdefghijklmnopqrstuvwxyz'
148 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
148 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
149 '0123456789' '_.-/')
149 '0123456789' '_.-/')
150 _safeset = None
150 _safeset = None
151 _hex = None
151 _hex = None
152 def quotepath(path):
152 def quotepath(path):
153 '''quote the path part of a URL
153 '''quote the path part of a URL
154
154
155 This is similar to urllib.quote, but it also tries to avoid
155 This is similar to urllib.quote, but it also tries to avoid
156 quoting things twice (inspired by wget):
156 quoting things twice (inspired by wget):
157
157
158 >>> quotepath('abc def')
158 >>> quotepath('abc def')
159 'abc%20def'
159 'abc%20def'
160 >>> quotepath('abc%20def')
160 >>> quotepath('abc%20def')
161 'abc%20def'
161 'abc%20def'
162 >>> quotepath('abc%20 def')
162 >>> quotepath('abc%20 def')
163 'abc%20%20def'
163 'abc%20%20def'
164 >>> quotepath('abc def%20')
164 >>> quotepath('abc def%20')
165 'abc%20def%20'
165 'abc%20def%20'
166 >>> quotepath('abc def%2')
166 >>> quotepath('abc def%2')
167 'abc%20def%252'
167 'abc%20def%252'
168 >>> quotepath('abc def%')
168 >>> quotepath('abc def%')
169 'abc%20def%25'
169 'abc%20def%25'
170 '''
170 '''
171 global _safeset, _hex
171 global _safeset, _hex
172 if _safeset is None:
172 if _safeset is None:
173 _safeset = util.set(_safe)
173 _safeset = util.set(_safe)
174 _hex = util.set('abcdefABCDEF0123456789')
174 _hex = util.set('abcdefABCDEF0123456789')
175 l = list(path)
175 l = list(path)
176 for i in xrange(len(l)):
176 for i in xrange(len(l)):
177 c = l[i]
177 c = l[i]
178 if c == '%' and i + 2 < len(l) and (l[i+1] in _hex and l[i+2] in _hex):
178 if c == '%' and i + 2 < len(l) and (l[i+1] in _hex and l[i+2] in _hex):
179 pass
179 pass
180 elif c not in _safeset:
180 elif c not in _safeset:
181 l[i] = '%%%02X' % ord(c)
181 l[i] = '%%%02X' % ord(c)
182 return ''.join(l)
182 return ''.join(l)
183
183
184 class httprepository(remoterepository):
184 class httprepository(remoterepository):
185 def __init__(self, ui, path):
185 def __init__(self, ui, path):
186 self.path = path
186 self.path = path
187 self.caps = None
187 self.caps = None
188 self.handler = None
188 self.handler = None
189 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
189 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
190 if query or frag:
190 if query or frag:
191 raise util.Abort(_('unsupported URL component: "%s"') %
191 raise util.Abort(_('unsupported URL component: "%s"') %
192 (query or frag))
192 (query or frag))
193 if not urlpath:
193 if not urlpath:
194 urlpath = '/'
194 urlpath = '/'
195 urlpath = quotepath(urlpath)
195 urlpath = quotepath(urlpath)
196 host, port, user, passwd = netlocsplit(netloc)
196 host, port, user, passwd = netlocsplit(netloc)
197
197
198 # urllib cannot handle URLs with embedded user or passwd
198 # urllib cannot handle URLs with embedded user or passwd
199 self._url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
199 self._url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
200 urlpath, '', ''))
200 urlpath, '', ''))
201 self.ui = ui
201 self.ui = ui
202 self.ui.debug(_('using %s\n') % self._url)
202 self.ui.debug(_('using %s\n') % self._url)
203
203
204 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
204 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
205 # XXX proxyauthinfo = None
205 # XXX proxyauthinfo = None
206 self.handler = httphandler()
206 self.handler = httphandler()
207 handlers = [self.handler]
207 handlers = [self.handler]
208
208
209 if proxyurl:
209 if proxyurl:
210 # proxy can be proper url or host[:port]
210 # proxy can be proper url or host[:port]
211 if not (proxyurl.startswith('http:') or
211 if not (proxyurl.startswith('http:') or
212 proxyurl.startswith('https:')):
212 proxyurl.startswith('https:')):
213 proxyurl = 'http://' + proxyurl + '/'
213 proxyurl = 'http://' + proxyurl + '/'
214 snpqf = urlparse.urlsplit(proxyurl)
214 snpqf = urlparse.urlsplit(proxyurl)
215 proxyscheme, proxynetloc, proxypath, proxyquery, proxyfrag = snpqf
215 proxyscheme, proxynetloc, proxypath, proxyquery, proxyfrag = snpqf
216 hpup = netlocsplit(proxynetloc)
216 hpup = netlocsplit(proxynetloc)
217
217
218 proxyhost, proxyport, proxyuser, proxypasswd = hpup
218 proxyhost, proxyport, proxyuser, proxypasswd = hpup
219 if not proxyuser:
219 if not proxyuser:
220 proxyuser = ui.config("http_proxy", "user")
220 proxyuser = ui.config("http_proxy", "user")
221 proxypasswd = ui.config("http_proxy", "passwd")
221 proxypasswd = ui.config("http_proxy", "passwd")
222
222
223 # see if we should use a proxy for this url
223 # see if we should use a proxy for this url
224 no_list = [ "localhost", "127.0.0.1" ]
224 no_list = [ "localhost", "127.0.0.1" ]
225 no_list.extend([p.lower() for
225 no_list.extend([p.lower() for
226 p in ui.configlist("http_proxy", "no")])
226 p in ui.configlist("http_proxy", "no")])
227 no_list.extend([p.strip().lower() for
227 no_list.extend([p.strip().lower() for
228 p in os.getenv("no_proxy", '').split(',')
228 p in os.getenv("no_proxy", '').split(',')
229 if p.strip()])
229 if p.strip()])
230 # "http_proxy.always" config is for running tests on localhost
230 # "http_proxy.always" config is for running tests on localhost
231 if (not ui.configbool("http_proxy", "always") and
231 if (not ui.configbool("http_proxy", "always") and
232 host.lower() in no_list):
232 host.lower() in no_list):
233 # avoid auto-detection of proxy settings by appending
234 # a ProxyHandler with no proxies defined.
235 handlers.append(urllib2.ProxyHandler({}))
233 ui.debug(_('disabling proxy for %s\n') % host)
236 ui.debug(_('disabling proxy for %s\n') % host)
234 else:
237 else:
235 proxyurl = urlparse.urlunsplit((
238 proxyurl = urlparse.urlunsplit((
236 proxyscheme, netlocunsplit(proxyhost, proxyport,
239 proxyscheme, netlocunsplit(proxyhost, proxyport,
237 proxyuser, proxypasswd or ''),
240 proxyuser, proxypasswd or ''),
238 proxypath, proxyquery, proxyfrag))
241 proxypath, proxyquery, proxyfrag))
239 handlers.append(urllib2.ProxyHandler({scheme: proxyurl}))
242 handlers.append(urllib2.ProxyHandler({scheme: proxyurl}))
240 ui.debug(_('proxying through http://%s:%s\n') %
243 ui.debug(_('proxying through http://%s:%s\n') %
241 (proxyhost, proxyport))
244 (proxyhost, proxyport))
242
245
243 # urllib2 takes proxy values from the environment and those
246 # urllib2 takes proxy values from the environment and those
244 # will take precedence if found, so drop them
247 # will take precedence if found, so drop them
245 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
248 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
246 try:
249 try:
247 if os.environ.has_key(env):
250 if os.environ.has_key(env):
248 del os.environ[env]
251 del os.environ[env]
249 except OSError:
252 except OSError:
250 pass
253 pass
251
254
252 passmgr = passwordmgr(ui)
255 passmgr = passwordmgr(ui)
253 if user:
256 if user:
254 ui.debug(_('http auth: user %s, password %s\n') %
257 ui.debug(_('http auth: user %s, password %s\n') %
255 (user, passwd and '*' * len(passwd) or 'not set'))
258 (user, passwd and '*' * len(passwd) or 'not set'))
256 passmgr.add_password(None, self._url, user, passwd or '')
259 passmgr.add_password(None, self._url, user, passwd or '')
257
260
258 handlers.extend((urllib2.HTTPBasicAuthHandler(passmgr),
261 handlers.extend((urllib2.HTTPBasicAuthHandler(passmgr),
259 httpdigestauthhandler(passmgr)))
262 httpdigestauthhandler(passmgr)))
260 opener = urllib2.build_opener(*handlers)
263 opener = urllib2.build_opener(*handlers)
261
264
262 # 1.0 here is the _protocol_ version
265 # 1.0 here is the _protocol_ version
263 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
266 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
264 urllib2.install_opener(opener)
267 urllib2.install_opener(opener)
265
268
266 def __del__(self):
269 def __del__(self):
267 if self.handler:
270 if self.handler:
268 self.handler.close_all()
271 self.handler.close_all()
269 self.handler = None
272 self.handler = None
270
273
271 def url(self):
274 def url(self):
272 return self.path
275 return self.path
273
276
274 # look up capabilities only when needed
277 # look up capabilities only when needed
275
278
276 def get_caps(self):
279 def get_caps(self):
277 if self.caps is None:
280 if self.caps is None:
278 try:
281 try:
279 self.caps = util.set(self.do_read('capabilities').split())
282 self.caps = util.set(self.do_read('capabilities').split())
280 except repo.RepoError:
283 except repo.RepoError:
281 self.caps = util.set()
284 self.caps = util.set()
282 self.ui.debug(_('capabilities: %s\n') %
285 self.ui.debug(_('capabilities: %s\n') %
283 (' '.join(self.caps or ['none'])))
286 (' '.join(self.caps or ['none'])))
284 return self.caps
287 return self.caps
285
288
286 capabilities = property(get_caps)
289 capabilities = property(get_caps)
287
290
288 def lock(self):
291 def lock(self):
289 raise util.Abort(_('operation not supported over http'))
292 raise util.Abort(_('operation not supported over http'))
290
293
291 def do_cmd(self, cmd, **args):
294 def do_cmd(self, cmd, **args):
292 data = args.pop('data', None)
295 data = args.pop('data', None)
293 headers = args.pop('headers', {})
296 headers = args.pop('headers', {})
294 self.ui.debug(_("sending %s command\n") % cmd)
297 self.ui.debug(_("sending %s command\n") % cmd)
295 q = {"cmd": cmd}
298 q = {"cmd": cmd}
296 q.update(args)
299 q.update(args)
297 qs = '?%s' % urllib.urlencode(q)
300 qs = '?%s' % urllib.urlencode(q)
298 cu = "%s%s" % (self._url, qs)
301 cu = "%s%s" % (self._url, qs)
299 try:
302 try:
300 if data:
303 if data:
301 self.ui.debug(_("sending %s bytes\n") % len(data))
304 self.ui.debug(_("sending %s bytes\n") % len(data))
302 resp = urllib2.urlopen(request(cu, data, headers))
305 resp = urllib2.urlopen(request(cu, data, headers))
303 except urllib2.HTTPError, inst:
306 except urllib2.HTTPError, inst:
304 if inst.code == 401:
307 if inst.code == 401:
305 raise util.Abort(_('authorization failed'))
308 raise util.Abort(_('authorization failed'))
306 raise
309 raise
307 except httplib.HTTPException, inst:
310 except httplib.HTTPException, inst:
308 self.ui.debug(_('http error while sending %s command\n') % cmd)
311 self.ui.debug(_('http error while sending %s command\n') % cmd)
309 self.ui.print_exc()
312 self.ui.print_exc()
310 raise IOError(None, inst)
313 raise IOError(None, inst)
311 except IndexError:
314 except IndexError:
312 # this only happens with Python 2.3, later versions raise URLError
315 # this only happens with Python 2.3, later versions raise URLError
313 raise util.Abort(_('http error, possibly caused by proxy setting'))
316 raise util.Abort(_('http error, possibly caused by proxy setting'))
314 # record the url we got redirected to
317 # record the url we got redirected to
315 resp_url = resp.geturl()
318 resp_url = resp.geturl()
316 if resp_url.endswith(qs):
319 if resp_url.endswith(qs):
317 resp_url = resp_url[:-len(qs)]
320 resp_url = resp_url[:-len(qs)]
318 if self._url != resp_url:
321 if self._url != resp_url:
319 self.ui.status(_('real URL is %s\n') % resp_url)
322 self.ui.status(_('real URL is %s\n') % resp_url)
320 self._url = resp_url
323 self._url = resp_url
321 try:
324 try:
322 proto = resp.getheader('content-type')
325 proto = resp.getheader('content-type')
323 except AttributeError:
326 except AttributeError:
324 proto = resp.headers['content-type']
327 proto = resp.headers['content-type']
325
328
326 # accept old "text/plain" and "application/hg-changegroup" for now
329 # accept old "text/plain" and "application/hg-changegroup" for now
327 if not (proto.startswith('application/mercurial-') or
330 if not (proto.startswith('application/mercurial-') or
328 proto.startswith('text/plain') or
331 proto.startswith('text/plain') or
329 proto.startswith('application/hg-changegroup')):
332 proto.startswith('application/hg-changegroup')):
330 self.ui.debug(_("Requested URL: '%s'\n") % cu)
333 self.ui.debug(_("Requested URL: '%s'\n") % cu)
331 raise repo.RepoError(_("'%s' does not appear to be an hg repository")
334 raise repo.RepoError(_("'%s' does not appear to be an hg repository")
332 % self._url)
335 % self._url)
333
336
334 if proto.startswith('application/mercurial-'):
337 if proto.startswith('application/mercurial-'):
335 try:
338 try:
336 version = proto.split('-', 1)[1]
339 version = proto.split('-', 1)[1]
337 version_info = tuple([int(n) for n in version.split('.')])
340 version_info = tuple([int(n) for n in version.split('.')])
338 except ValueError:
341 except ValueError:
339 raise repo.RepoError(_("'%s' sent a broken Content-type "
342 raise repo.RepoError(_("'%s' sent a broken Content-type "
340 "header (%s)") % (self._url, proto))
343 "header (%s)") % (self._url, proto))
341 if version_info > (0, 1):
344 if version_info > (0, 1):
342 raise repo.RepoError(_("'%s' uses newer protocol %s") %
345 raise repo.RepoError(_("'%s' uses newer protocol %s") %
343 (self._url, version))
346 (self._url, version))
344
347
345 return resp
348 return resp
346
349
347 def do_read(self, cmd, **args):
350 def do_read(self, cmd, **args):
348 fp = self.do_cmd(cmd, **args)
351 fp = self.do_cmd(cmd, **args)
349 try:
352 try:
350 return fp.read()
353 return fp.read()
351 finally:
354 finally:
352 # if using keepalive, allow connection to be reused
355 # if using keepalive, allow connection to be reused
353 fp.close()
356 fp.close()
354
357
355 def lookup(self, key):
358 def lookup(self, key):
356 self.requirecap('lookup', _('look up remote revision'))
359 self.requirecap('lookup', _('look up remote revision'))
357 d = self.do_cmd("lookup", key = key).read()
360 d = self.do_cmd("lookup", key = key).read()
358 success, data = d[:-1].split(' ', 1)
361 success, data = d[:-1].split(' ', 1)
359 if int(success):
362 if int(success):
360 return bin(data)
363 return bin(data)
361 raise repo.RepoError(data)
364 raise repo.RepoError(data)
362
365
363 def heads(self):
366 def heads(self):
364 d = self.do_read("heads")
367 d = self.do_read("heads")
365 try:
368 try:
366 return map(bin, d[:-1].split(" "))
369 return map(bin, d[:-1].split(" "))
367 except:
370 except:
368 raise util.UnexpectedOutput(_("unexpected response:"), d)
371 raise util.UnexpectedOutput(_("unexpected response:"), d)
369
372
370 def branches(self, nodes):
373 def branches(self, nodes):
371 n = " ".join(map(hex, nodes))
374 n = " ".join(map(hex, nodes))
372 d = self.do_read("branches", nodes=n)
375 d = self.do_read("branches", nodes=n)
373 try:
376 try:
374 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
377 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
375 return br
378 return br
376 except:
379 except:
377 raise util.UnexpectedOutput(_("unexpected response:"), d)
380 raise util.UnexpectedOutput(_("unexpected response:"), d)
378
381
379 def between(self, pairs):
382 def between(self, pairs):
380 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
383 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
381 d = self.do_read("between", pairs=n)
384 d = self.do_read("between", pairs=n)
382 try:
385 try:
383 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
386 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
384 return p
387 return p
385 except:
388 except:
386 raise util.UnexpectedOutput(_("unexpected response:"), d)
389 raise util.UnexpectedOutput(_("unexpected response:"), d)
387
390
388 def changegroup(self, nodes, kind):
391 def changegroup(self, nodes, kind):
389 n = " ".join(map(hex, nodes))
392 n = " ".join(map(hex, nodes))
390 f = self.do_cmd("changegroup", roots=n)
393 f = self.do_cmd("changegroup", roots=n)
391 return util.chunkbuffer(zgenerator(f))
394 return util.chunkbuffer(zgenerator(f))
392
395
393 def changegroupsubset(self, bases, heads, source):
396 def changegroupsubset(self, bases, heads, source):
394 self.requirecap('changegroupsubset', _('look up remote changes'))
397 self.requirecap('changegroupsubset', _('look up remote changes'))
395 baselst = " ".join([hex(n) for n in bases])
398 baselst = " ".join([hex(n) for n in bases])
396 headlst = " ".join([hex(n) for n in heads])
399 headlst = " ".join([hex(n) for n in heads])
397 f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst)
400 f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst)
398 return util.chunkbuffer(zgenerator(f))
401 return util.chunkbuffer(zgenerator(f))
399
402
400 def unbundle(self, cg, heads, source):
403 def unbundle(self, cg, heads, source):
401 # have to stream bundle to a temp file because we do not have
404 # have to stream bundle to a temp file because we do not have
402 # http 1.1 chunked transfer.
405 # http 1.1 chunked transfer.
403
406
404 type = ""
407 type = ""
405 types = self.capable('unbundle')
408 types = self.capable('unbundle')
406 # servers older than d1b16a746db6 will send 'unbundle' as a
409 # servers older than d1b16a746db6 will send 'unbundle' as a
407 # boolean capability
410 # boolean capability
408 try:
411 try:
409 types = types.split(',')
412 types = types.split(',')
410 except AttributeError:
413 except AttributeError:
411 types = [""]
414 types = [""]
412 if types:
415 if types:
413 for x in types:
416 for x in types:
414 if x in changegroup.bundletypes:
417 if x in changegroup.bundletypes:
415 type = x
418 type = x
416 break
419 break
417
420
418 tempname = changegroup.writebundle(cg, None, type)
421 tempname = changegroup.writebundle(cg, None, type)
419 fp = httpsendfile(tempname, "rb")
422 fp = httpsendfile(tempname, "rb")
420 try:
423 try:
421 try:
424 try:
422 rfp = self.do_cmd(
425 rfp = self.do_cmd(
423 'unbundle', data=fp,
426 'unbundle', data=fp,
424 headers={'content-type': 'application/octet-stream'},
427 headers={'content-type': 'application/octet-stream'},
425 heads=' '.join(map(hex, heads)))
428 heads=' '.join(map(hex, heads)))
426 try:
429 try:
427 ret = int(rfp.readline())
430 ret = int(rfp.readline())
428 self.ui.write(rfp.read())
431 self.ui.write(rfp.read())
429 return ret
432 return ret
430 finally:
433 finally:
431 rfp.close()
434 rfp.close()
432 except socket.error, err:
435 except socket.error, err:
433 if err[0] in (errno.ECONNRESET, errno.EPIPE):
436 if err[0] in (errno.ECONNRESET, errno.EPIPE):
434 raise util.Abort(_('push failed: %s') % err[1])
437 raise util.Abort(_('push failed: %s') % err[1])
435 raise util.Abort(err[1])
438 raise util.Abort(err[1])
436 finally:
439 finally:
437 fp.close()
440 fp.close()
438 os.unlink(tempname)
441 os.unlink(tempname)
439
442
440 def stream_out(self):
443 def stream_out(self):
441 return self.do_cmd('stream_out')
444 return self.do_cmd('stream_out')
442
445
443 class httpsrepository(httprepository):
446 class httpsrepository(httprepository):
444 def __init__(self, ui, path):
447 def __init__(self, ui, path):
445 if not has_https:
448 if not has_https:
446 raise util.Abort(_('Python support for SSL and HTTPS '
449 raise util.Abort(_('Python support for SSL and HTTPS '
447 'is not installed'))
450 'is not installed'))
448 httprepository.__init__(self, ui, path)
451 httprepository.__init__(self, ui, path)
449
452
450 def instance(ui, path, create):
453 def instance(ui, path, create):
451 if create:
454 if create:
452 raise util.Abort(_('cannot create new http repository'))
455 raise util.Abort(_('cannot create new http repository'))
453 if path.startswith('https:'):
456 if path.startswith('https:'):
454 return httpsrepository(ui, path)
457 return httpsrepository(ui, path)
455 return httprepository(ui, path)
458 return httprepository(ui, path)
General Comments 0
You need to be logged in to leave comments. Login now