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