##// END OF EJS Templates
Client support for hgweb unbundle with versions.
Thomas Arendsen Hein -
r3613:cbf352b9 default
parent child Browse files
Show More
@@ -1,404 +1,408 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 handlers = [httphandler()]
135 handlers = [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 handlers.append(urllib2.ProxyHandler({scheme: proxyurl}))
167 handlers.append(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 handlers.extend((urllib2.HTTPBasicAuthHandler(passmgr),
186 handlers.extend((urllib2.HTTPBasicAuthHandler(passmgr),
187 urllib2.HTTPDigestAuthHandler(passmgr)))
187 urllib2.HTTPDigestAuthHandler(passmgr)))
188 opener = urllib2.build_opener(*handlers)
188 opener = urllib2.build_opener(*handlers)
189
189
190 # 1.0 here is the _protocol_ version
190 # 1.0 here is the _protocol_ version
191 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
191 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
192 urllib2.install_opener(opener)
192 urllib2.install_opener(opener)
193
193
194 def url(self):
194 def url(self):
195 return self.path
195 return self.path
196
196
197 # look up capabilities only when needed
197 # look up capabilities only when needed
198
198
199 def get_caps(self):
199 def get_caps(self):
200 if self.caps is None:
200 if self.caps is None:
201 try:
201 try:
202 self.caps = self.do_read('capabilities').split()
202 self.caps = self.do_read('capabilities').split()
203 except hg.RepoError:
203 except hg.RepoError:
204 self.caps = ()
204 self.caps = ()
205 self.ui.debug(_('capabilities: %s\n') %
205 self.ui.debug(_('capabilities: %s\n') %
206 (' '.join(self.caps or ['none'])))
206 (' '.join(self.caps or ['none'])))
207 return self.caps
207 return self.caps
208
208
209 capabilities = property(get_caps)
209 capabilities = property(get_caps)
210
210
211 def lock(self):
211 def lock(self):
212 raise util.Abort(_('operation not supported over http'))
212 raise util.Abort(_('operation not supported over http'))
213
213
214 def do_cmd(self, cmd, **args):
214 def do_cmd(self, cmd, **args):
215 data = args.pop('data', None)
215 data = args.pop('data', None)
216 headers = args.pop('headers', {})
216 headers = args.pop('headers', {})
217 self.ui.debug(_("sending %s command\n") % cmd)
217 self.ui.debug(_("sending %s command\n") % cmd)
218 q = {"cmd": cmd}
218 q = {"cmd": cmd}
219 q.update(args)
219 q.update(args)
220 qs = '?%s' % urllib.urlencode(q)
220 qs = '?%s' % urllib.urlencode(q)
221 cu = "%s%s" % (self._url, qs)
221 cu = "%s%s" % (self._url, qs)
222 try:
222 try:
223 if data:
223 if data:
224 self.ui.debug(_("sending %s bytes\n") %
224 self.ui.debug(_("sending %s bytes\n") %
225 headers.get('content-length', 'X'))
225 headers.get('content-length', 'X'))
226 resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
226 resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
227 except urllib2.HTTPError, inst:
227 except urllib2.HTTPError, inst:
228 if inst.code == 401:
228 if inst.code == 401:
229 raise util.Abort(_('authorization failed'))
229 raise util.Abort(_('authorization failed'))
230 raise
230 raise
231 except httplib.HTTPException, inst:
231 except httplib.HTTPException, inst:
232 self.ui.debug(_('http error while sending %s command\n') % cmd)
232 self.ui.debug(_('http error while sending %s command\n') % cmd)
233 self.ui.print_exc()
233 self.ui.print_exc()
234 raise IOError(None, inst)
234 raise IOError(None, inst)
235 except IndexError:
235 except IndexError:
236 # this only happens with Python 2.3, later versions raise URLError
236 # this only happens with Python 2.3, later versions raise URLError
237 raise util.Abort(_('http error, possibly caused by proxy setting'))
237 raise util.Abort(_('http error, possibly caused by proxy setting'))
238 # record the url we got redirected to
238 # record the url we got redirected to
239 resp_url = resp.geturl()
239 resp_url = resp.geturl()
240 if resp_url.endswith(qs):
240 if resp_url.endswith(qs):
241 resp_url = resp_url[:-len(qs)]
241 resp_url = resp_url[:-len(qs)]
242 if self._url != resp_url:
242 if self._url != resp_url:
243 self.ui.status(_('real URL is %s\n') % resp_url)
243 self.ui.status(_('real URL is %s\n') % resp_url)
244 self._url = resp_url
244 self._url = resp_url
245 try:
245 try:
246 proto = resp.getheader('content-type')
246 proto = resp.getheader('content-type')
247 except AttributeError:
247 except AttributeError:
248 proto = resp.headers['content-type']
248 proto = resp.headers['content-type']
249
249
250 # accept old "text/plain" and "application/hg-changegroup" for now
250 # accept old "text/plain" and "application/hg-changegroup" for now
251 if not proto.startswith('application/mercurial') and \
251 if not proto.startswith('application/mercurial') and \
252 not proto.startswith('text/plain') and \
252 not proto.startswith('text/plain') and \
253 not proto.startswith('application/hg-changegroup'):
253 not proto.startswith('application/hg-changegroup'):
254 raise hg.RepoError(_("'%s' does not appear to be an hg repository") %
254 raise hg.RepoError(_("'%s' does not appear to be an hg repository") %
255 self._url)
255 self._url)
256
256
257 if proto.startswith('application/mercurial'):
257 if proto.startswith('application/mercurial'):
258 version = proto[22:]
258 version = proto[22:]
259 if float(version) > 0.1:
259 if float(version) > 0.1:
260 raise hg.RepoError(_("'%s' uses newer protocol %s") %
260 raise hg.RepoError(_("'%s' uses newer protocol %s") %
261 (self._url, version))
261 (self._url, version))
262
262
263 return resp
263 return resp
264
264
265 def do_read(self, cmd, **args):
265 def do_read(self, cmd, **args):
266 fp = self.do_cmd(cmd, **args)
266 fp = self.do_cmd(cmd, **args)
267 try:
267 try:
268 return fp.read()
268 return fp.read()
269 finally:
269 finally:
270 # if using keepalive, allow connection to be reused
270 # if using keepalive, allow connection to be reused
271 fp.close()
271 fp.close()
272
272
273 def lookup(self, key):
273 def lookup(self, key):
274 d = self.do_cmd("lookup", key = key).read()
274 d = self.do_cmd("lookup", key = key).read()
275 success, data = d[:-1].split(' ', 1)
275 success, data = d[:-1].split(' ', 1)
276 if int(success):
276 if int(success):
277 return bin(data)
277 return bin(data)
278 raise hg.RepoError(data)
278 raise hg.RepoError(data)
279
279
280 def heads(self):
280 def heads(self):
281 d = self.do_read("heads")
281 d = self.do_read("heads")
282 try:
282 try:
283 return map(bin, d[:-1].split(" "))
283 return map(bin, d[:-1].split(" "))
284 except:
284 except:
285 raise util.UnexpectedOutput(_("unexpected response:"), d)
285 raise util.UnexpectedOutput(_("unexpected response:"), d)
286
286
287 def branches(self, nodes):
287 def branches(self, nodes):
288 n = " ".join(map(hex, nodes))
288 n = " ".join(map(hex, nodes))
289 d = self.do_read("branches", nodes=n)
289 d = self.do_read("branches", nodes=n)
290 try:
290 try:
291 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
291 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
292 return br
292 return br
293 except:
293 except:
294 raise util.UnexpectedOutput(_("unexpected response:"), d)
294 raise util.UnexpectedOutput(_("unexpected response:"), d)
295
295
296 def between(self, pairs):
296 def between(self, pairs):
297 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
297 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
298 d = self.do_read("between", pairs=n)
298 d = self.do_read("between", pairs=n)
299 try:
299 try:
300 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
300 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
301 return p
301 return p
302 except:
302 except:
303 raise util.UnexpectedOutput(_("unexpected response:"), d)
303 raise util.UnexpectedOutput(_("unexpected response:"), d)
304
304
305 def changegroup(self, nodes, kind):
305 def changegroup(self, nodes, kind):
306 n = " ".join(map(hex, nodes))
306 n = " ".join(map(hex, nodes))
307 f = self.do_cmd("changegroup", roots=n)
307 f = self.do_cmd("changegroup", roots=n)
308
308
309 def zgenerator(f):
309 def zgenerator(f):
310 zd = zlib.decompressobj()
310 zd = zlib.decompressobj()
311 try:
311 try:
312 for chnk in f:
312 for chnk in f:
313 yield zd.decompress(chnk)
313 yield zd.decompress(chnk)
314 except httplib.HTTPException, inst:
314 except httplib.HTTPException, inst:
315 raise IOError(None, _('connection ended unexpectedly'))
315 raise IOError(None, _('connection ended unexpectedly'))
316 yield zd.flush()
316 yield zd.flush()
317
317
318 return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
318 return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
319
319
320 def changegroupsubset(self, bases, heads, source):
320 def changegroupsubset(self, bases, heads, source):
321 baselst = " ".join([hex(n) for n in bases])
321 baselst = " ".join([hex(n) for n in bases])
322 headlst = " ".join([hex(n) for n in heads])
322 headlst = " ".join([hex(n) for n in heads])
323 f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst)
323 f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst)
324
324
325 def zgenerator(f):
325 def zgenerator(f):
326 zd = zlib.decompressobj()
326 zd = zlib.decompressobj()
327 try:
327 try:
328 for chnk in f:
328 for chnk in f:
329 yield zd.decompress(chnk)
329 yield zd.decompress(chnk)
330 except httplib.HTTPException:
330 except httplib.HTTPException:
331 raise IOError(None, _('connection ended unexpectedly'))
331 raise IOError(None, _('connection ended unexpectedly'))
332 yield zd.flush()
332 yield zd.flush()
333
333
334 return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
334 return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
335
335
336 def unbundle(self, cg, heads, source):
336 def unbundle(self, cg, heads, source):
337 # have to stream bundle to a temp file because we do not have
337 # have to stream bundle to a temp file because we do not have
338 # http 1.1 chunked transfer.
338 # http 1.1 chunked transfer.
339
339
340 use_compress = 'standardbundle' in self.capabilities
341
342 # XXX duplication from commands.py
340 # XXX duplication from commands.py
343 class nocompress(object):
341 class nocompress(object):
344 def compress(self, x):
342 def compress(self, x):
345 return x
343 return x
346 def flush(self):
344 def flush(self):
347 return ""
345 return ""
348
346
349 if use_compress:
347 unbundleversions = self.capable('unbundle')
348 try:
349 unbundleversions = unbundleversions.split(',')
350 except AttributeError:
351 unbundleversions = [""]
352
353 if "HG10GZ" in unbundleversions:
350 header = "HG10GZ"
354 header = "HG10GZ"
351 z = zlib.compressobj()
355 z = zlib.compressobj()
352 else:
356 else:
353 self.ui.note(_("server has no compression support, "
357 self.ui.note(_("server has no compression support, "
354 "sending uncompressed"))
358 "sending uncompressed"))
355 header = ""
359 header = ""
356 z = nocompress()
360 z = nocompress()
357
361
358 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
362 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
359 fp = os.fdopen(fd, 'wb+')
363 fp = os.fdopen(fd, 'wb+')
360 try:
364 try:
361 fp.write(header)
365 fp.write(header)
362 for chunk in util.filechunkiter(cg):
366 for chunk in util.filechunkiter(cg):
363 fp.write(z.compress(chunk))
367 fp.write(z.compress(chunk))
364 fp.write(z.flush())
368 fp.write(z.flush())
365 length = fp.tell()
369 length = fp.tell()
366 try:
370 try:
367 rfp = self.do_cmd(
371 rfp = self.do_cmd(
368 'unbundle', data=fp,
372 'unbundle', data=fp,
369 headers={'content-length': str(length),
373 headers={'content-length': str(length),
370 'content-type': 'application/octet-stream'},
374 'content-type': 'application/octet-stream'},
371 heads=' '.join(map(hex, heads)))
375 heads=' '.join(map(hex, heads)))
372 try:
376 try:
373 ret = int(rfp.readline())
377 ret = int(rfp.readline())
374 self.ui.write(rfp.read())
378 self.ui.write(rfp.read())
375 return ret
379 return ret
376 finally:
380 finally:
377 rfp.close()
381 rfp.close()
378 except socket.error, err:
382 except socket.error, err:
379 if err[0] in (errno.ECONNRESET, errno.EPIPE):
383 if err[0] in (errno.ECONNRESET, errno.EPIPE):
380 raise util.Abort(_('push failed: %s') % err[1])
384 raise util.Abort(_('push failed: %s') % err[1])
381 raise util.Abort(err[1])
385 raise util.Abort(err[1])
382 finally:
386 finally:
383 fp.close()
387 fp.close()
384 os.unlink(tempname)
388 os.unlink(tempname)
385
389
386 def stream_out(self):
390 def stream_out(self):
387 return self.do_cmd('stream_out')
391 return self.do_cmd('stream_out')
388
392
389 class httpsrepository(httprepository):
393 class httpsrepository(httprepository):
390 def __init__(self, ui, path):
394 def __init__(self, ui, path):
391 if not has_https:
395 if not has_https:
392 raise util.Abort(_('Python support for SSL and HTTPS '
396 raise util.Abort(_('Python support for SSL and HTTPS '
393 'is not installed'))
397 'is not installed'))
394 httprepository.__init__(self, ui, path)
398 httprepository.__init__(self, ui, path)
395
399
396 def instance(ui, path, create):
400 def instance(ui, path, create):
397 if create:
401 if create:
398 raise util.Abort(_('cannot create new http repository'))
402 raise util.Abort(_('cannot create new http repository'))
399 if path.startswith('hg:'):
403 if path.startswith('hg:'):
400 ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
404 ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
401 path = 'http:' + path[3:]
405 path = 'http:' + path[3:]
402 if path.startswith('https:'):
406 if path.startswith('https:'):
403 return httpsrepository(ui, path)
407 return httpsrepository(ui, path)
404 return httprepository(ui, path)
408 return httprepository(ui, path)
General Comments 0
You need to be logged in to leave comments. Login now