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