comparison mercurial/httprepo.py @ 11587:a036f6bd1da3

protocol: unify basic http client requests
author Matt Mackall <mpm@selenic.com>
date Wed, 14 Jul 2010 16:35:51 -0500
parents 34cc8b84407f
children 8a1f625e971d
comparison
equal deleted inserted replaced
11586:ddaaaa23bb8f 11587:a036f6bd1da3
6 # This software may be used and distributed according to the terms of the 6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version. 7 # GNU General Public License version 2 or any later version.
8 8
9 from node import bin, hex, nullid 9 from node import bin, hex, nullid
10 from i18n import _ 10 from i18n import _
11 import repo, changegroup, statichttprepo, error, url, util, pushkey 11 import repo, changegroup, statichttprepo, error, url, util, wireproto
12 import os, urllib, urllib2, urlparse, zlib, httplib 12 import os, urllib, urllib2, urlparse, zlib, httplib
13 import errno, socket 13 import errno, socket
14 import encoding 14 import encoding
15 15
16 def zgenerator(f): 16 def zgenerator(f):
20 yield zd.decompress(chunk) 20 yield zd.decompress(chunk)
21 except httplib.HTTPException: 21 except httplib.HTTPException:
22 raise IOError(None, _('connection ended unexpectedly')) 22 raise IOError(None, _('connection ended unexpectedly'))
23 yield zd.flush() 23 yield zd.flush()
24 24
25 class httprepository(repo.repository): 25 class httprepository(wireproto.wirerepository):
26 def __init__(self, ui, path): 26 def __init__(self, ui, path):
27 self.path = path 27 self.path = path
28 self.caps = None 28 self.caps = None
29 self.handler = None 29 self.handler = None
30 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path) 30 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
136 return fp.read() 136 return fp.read()
137 finally: 137 finally:
138 # if using keepalive, allow connection to be reused 138 # if using keepalive, allow connection to be reused
139 fp.close() 139 fp.close()
140 140
141 def lookup(self, key): 141 def _call(self, cmd, **args):
142 self.requirecap('lookup', _('look up remote revision')) 142 return self.do_read(cmd, **args)
143 d = self.do_cmd("lookup", key = key).read() 143
144 success, data = d[:-1].split(' ', 1) 144 def _abort(self, exception):
145 if int(success): 145 raise exception
146 return bin(data)
147 raise error.RepoError(data)
148
149 def heads(self):
150 d = self.do_read("heads")
151 try:
152 return map(bin, d[:-1].split(" "))
153 except:
154 raise error.ResponseError(_("unexpected response:"), d)
155
156 def branchmap(self):
157 d = self.do_read("branchmap")
158 try:
159 branchmap = {}
160 for branchpart in d.splitlines():
161 branchheads = branchpart.split(' ')
162 branchname = urllib.unquote(branchheads[0])
163 # Earlier servers (1.3.x) send branch names in (their) local
164 # charset. The best we can do is assume it's identical to our
165 # own local charset, in case it's not utf-8.
166 try:
167 branchname.decode('utf-8')
168 except UnicodeDecodeError:
169 branchname = encoding.fromlocal(branchname)
170 branchheads = [bin(x) for x in branchheads[1:]]
171 branchmap[branchname] = branchheads
172 return branchmap
173 except:
174 raise error.ResponseError(_("unexpected response:"), d)
175
176 def branches(self, nodes):
177 n = " ".join(map(hex, nodes))
178 d = self.do_read("branches", nodes=n)
179 try:
180 br = [tuple(map(bin, b.split(" "))) for b in d.splitlines()]
181 return br
182 except:
183 raise error.ResponseError(_("unexpected response:"), d)
184
185 def between(self, pairs):
186 batch = 8 # avoid giant requests
187 r = []
188 for i in xrange(0, len(pairs), batch):
189 n = " ".join(["-".join(map(hex, p)) for p in pairs[i:i + batch]])
190 d = self.do_read("between", pairs=n)
191 try:
192 r += [l and map(bin, l.split(" ")) or []
193 for l in d.splitlines()]
194 except:
195 raise error.ResponseError(_("unexpected response:"), d)
196 return r
197 146
198 def changegroup(self, nodes, kind): 147 def changegroup(self, nodes, kind):
199 n = " ".join(map(hex, nodes)) 148 n = " ".join(map(hex, nodes))
200 f = self.do_cmd("changegroup", roots=n) 149 f = self.do_cmd("changegroup", roots=n)
201 return util.chunkbuffer(zgenerator(f)) 150 return util.chunkbuffer(zgenerator(f))
257 os.unlink(tempname) 206 os.unlink(tempname)
258 207
259 def stream_out(self): 208 def stream_out(self):
260 return self.do_cmd('stream_out') 209 return self.do_cmd('stream_out')
261 210
262 def pushkey(self, namespace, key, old, new):
263 if not self.capable('pushkey'):
264 return False
265 d = self.do_cmd("pushkey", data="", # force a POST
266 namespace=namespace, key=key, old=old, new=new).read()
267 code, output = d.split('\n', 1)
268 try:
269 ret = bool(int(code))
270 except ValueError, err:
271 raise error.ResponseError(
272 _('push failed (unexpected response):'), d)
273 for l in output.splitlines(True):
274 self.ui.status(_('remote: '), l)
275 return ret
276
277 def listkeys(self, namespace):
278 if not self.capable('pushkey'):
279 return {}
280 d = self.do_cmd("listkeys", namespace=namespace).read()
281 r = {}
282 for l in d.splitlines():
283 k, v = l.split('\t')
284 r[k.decode('string-escape')] = v.decode('string-escape')
285 return r
286
287 class httpsrepository(httprepository): 211 class httpsrepository(httprepository):
288 def __init__(self, ui, path): 212 def __init__(self, ui, path):
289 if not url.has_https: 213 if not url.has_https:
290 raise util.Abort(_('Python support for SSL and HTTPS ' 214 raise util.Abort(_('Python support for SSL and HTTPS '
291 'is not installed')) 215 'is not installed'))