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