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')) |