##// END OF EJS Templates
httprepo: normalize output from unbundle with ssh...
Sune Foldager -
r10544:2e1a9b81 stable
parent child Browse files
Show More
@@ -1,274 +1,275 b''
1 1 # httprepo.py - HTTP repository proxy classes for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 from node import bin, hex, nullid
10 10 from i18n import _
11 11 import repo, changegroup, statichttprepo, error, url, util
12 12 import os, urllib, urllib2, urlparse, zlib, httplib
13 13 import errno, socket
14 14 import encoding
15 15
16 16 def zgenerator(f):
17 17 zd = zlib.decompressobj()
18 18 try:
19 19 for chunk in util.filechunkiter(f):
20 20 yield zd.decompress(chunk)
21 21 except httplib.HTTPException:
22 22 raise IOError(None, _('connection ended unexpectedly'))
23 23 yield zd.flush()
24 24
25 25 class httprepository(repo.repository):
26 26 def __init__(self, ui, path):
27 27 self.path = path
28 28 self.caps = None
29 29 self.handler = None
30 30 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
31 31 if query or frag:
32 32 raise util.Abort(_('unsupported URL component: "%s"') %
33 33 (query or frag))
34 34
35 35 # urllib cannot handle URLs with embedded user or passwd
36 36 self._url, authinfo = url.getauthinfo(path)
37 37
38 38 self.ui = ui
39 39 self.ui.debug('using %s\n' % self._url)
40 40
41 41 self.urlopener = url.opener(ui, authinfo)
42 42
43 43 def __del__(self):
44 44 for h in self.urlopener.handlers:
45 45 h.close()
46 46 if hasattr(h, "close_all"):
47 47 h.close_all()
48 48
49 49 def url(self):
50 50 return self.path
51 51
52 52 # look up capabilities only when needed
53 53
54 54 def get_caps(self):
55 55 if self.caps is None:
56 56 try:
57 57 self.caps = set(self.do_read('capabilities').split())
58 58 except error.RepoError:
59 59 self.caps = set()
60 60 self.ui.debug('capabilities: %s\n' %
61 61 (' '.join(self.caps or ['none'])))
62 62 return self.caps
63 63
64 64 capabilities = property(get_caps)
65 65
66 66 def lock(self):
67 67 raise util.Abort(_('operation not supported over http'))
68 68
69 69 def do_cmd(self, cmd, **args):
70 70 data = args.pop('data', None)
71 71 headers = args.pop('headers', {})
72 72 self.ui.debug("sending %s command\n" % cmd)
73 73 q = {"cmd": cmd}
74 74 q.update(args)
75 75 qs = '?%s' % urllib.urlencode(q)
76 76 cu = "%s%s" % (self._url, qs)
77 77 req = urllib2.Request(cu, data, headers)
78 78 if data is not None:
79 79 # len(data) is broken if data doesn't fit into Py_ssize_t
80 80 # add the header ourself to avoid OverflowError
81 81 size = data.__len__()
82 82 self.ui.debug("sending %s bytes\n" % size)
83 83 req.add_unredirected_header('Content-Length', '%d' % size)
84 84 try:
85 85 resp = self.urlopener.open(req)
86 86 except urllib2.HTTPError, inst:
87 87 if inst.code == 401:
88 88 raise util.Abort(_('authorization failed'))
89 89 raise
90 90 except httplib.HTTPException, inst:
91 91 self.ui.debug('http error while sending %s command\n' % cmd)
92 92 self.ui.traceback()
93 93 raise IOError(None, inst)
94 94 except IndexError:
95 95 # this only happens with Python 2.3, later versions raise URLError
96 96 raise util.Abort(_('http error, possibly caused by proxy setting'))
97 97 # record the url we got redirected to
98 98 resp_url = resp.geturl()
99 99 if resp_url.endswith(qs):
100 100 resp_url = resp_url[:-len(qs)]
101 101 if self._url.rstrip('/') != resp_url.rstrip('/'):
102 102 self.ui.status(_('real URL is %s\n') % resp_url)
103 103 self._url = resp_url
104 104 try:
105 105 proto = resp.getheader('content-type')
106 106 except AttributeError:
107 107 proto = resp.headers['content-type']
108 108
109 109 safeurl = url.hidepassword(self._url)
110 110 # accept old "text/plain" and "application/hg-changegroup" for now
111 111 if not (proto.startswith('application/mercurial-') or
112 112 proto.startswith('text/plain') or
113 113 proto.startswith('application/hg-changegroup')):
114 114 self.ui.debug("requested URL: '%s'\n" % url.hidepassword(cu))
115 115 raise error.RepoError(
116 116 _("'%s' does not appear to be an hg repository:\n"
117 117 "---%%<--- (%s)\n%s\n---%%<---\n")
118 118 % (safeurl, proto, resp.read()))
119 119
120 120 if proto.startswith('application/mercurial-'):
121 121 try:
122 122 version = proto.split('-', 1)[1]
123 123 version_info = tuple([int(n) for n in version.split('.')])
124 124 except ValueError:
125 125 raise error.RepoError(_("'%s' sent a broken Content-Type "
126 126 "header (%s)") % (safeurl, proto))
127 127 if version_info > (0, 1):
128 128 raise error.RepoError(_("'%s' uses newer protocol %s") %
129 129 (safeurl, version))
130 130
131 131 return resp
132 132
133 133 def do_read(self, cmd, **args):
134 134 fp = self.do_cmd(cmd, **args)
135 135 try:
136 136 return fp.read()
137 137 finally:
138 138 # if using keepalive, allow connection to be reused
139 139 fp.close()
140 140
141 141 def lookup(self, key):
142 142 self.requirecap('lookup', _('look up remote revision'))
143 143 d = self.do_cmd("lookup", key = key).read()
144 144 success, data = d[:-1].split(' ', 1)
145 145 if int(success):
146 146 return bin(data)
147 147 raise error.RepoError(data)
148 148
149 149 def heads(self):
150 150 d = self.do_read("heads")
151 151 try:
152 152 return map(bin, d[:-1].split(" "))
153 153 except:
154 154 raise error.ResponseError(_("unexpected response:"), d)
155 155
156 156 def branchmap(self):
157 157 d = self.do_read("branchmap")
158 158 try:
159 159 branchmap = {}
160 160 for branchpart in d.splitlines():
161 161 branchheads = branchpart.split(' ')
162 162 branchname = urllib.unquote(branchheads[0])
163 163 # Earlier servers (1.3.x) send branch names in (their) local
164 164 # charset. The best we can do is assume it's identical to our
165 165 # own local charset, in case it's not utf-8.
166 166 try:
167 167 branchname.decode('utf-8')
168 168 except UnicodeDecodeError:
169 169 branchname = encoding.fromlocal(branchname)
170 170 branchheads = [bin(x) for x in branchheads[1:]]
171 171 branchmap[branchname] = branchheads
172 172 return branchmap
173 173 except:
174 174 raise error.ResponseError(_("unexpected response:"), d)
175 175
176 176 def branches(self, nodes):
177 177 n = " ".join(map(hex, nodes))
178 178 d = self.do_read("branches", nodes=n)
179 179 try:
180 180 br = [tuple(map(bin, b.split(" "))) for b in d.splitlines()]
181 181 return br
182 182 except:
183 183 raise error.ResponseError(_("unexpected response:"), d)
184 184
185 185 def between(self, pairs):
186 186 batch = 8 # avoid giant requests
187 187 r = []
188 188 for i in xrange(0, len(pairs), batch):
189 189 n = " ".join(["-".join(map(hex, p)) for p in pairs[i:i + batch]])
190 190 d = self.do_read("between", pairs=n)
191 191 try:
192 192 r += [l and map(bin, l.split(" ")) or []
193 193 for l in d.splitlines()]
194 194 except:
195 195 raise error.ResponseError(_("unexpected response:"), d)
196 196 return r
197 197
198 198 def changegroup(self, nodes, kind):
199 199 n = " ".join(map(hex, nodes))
200 200 f = self.do_cmd("changegroup", roots=n)
201 201 return util.chunkbuffer(zgenerator(f))
202 202
203 203 def changegroupsubset(self, bases, heads, source):
204 204 self.requirecap('changegroupsubset', _('look up remote changes'))
205 205 baselst = " ".join([hex(n) for n in bases])
206 206 headlst = " ".join([hex(n) for n in heads])
207 207 f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst)
208 208 return util.chunkbuffer(zgenerator(f))
209 209
210 210 def unbundle(self, cg, heads, source):
211 211 # have to stream bundle to a temp file because we do not have
212 212 # http 1.1 chunked transfer.
213 213
214 214 type = ""
215 215 types = self.capable('unbundle')
216 216 # servers older than d1b16a746db6 will send 'unbundle' as a
217 217 # boolean capability
218 218 try:
219 219 types = types.split(',')
220 220 except AttributeError:
221 221 types = [""]
222 222 if types:
223 223 for x in types:
224 224 if x in changegroup.bundletypes:
225 225 type = x
226 226 break
227 227
228 228 tempname = changegroup.writebundle(cg, None, type)
229 229 fp = url.httpsendfile(tempname, "rb")
230 230 try:
231 231 try:
232 232 resp = self.do_read(
233 233 'unbundle', data=fp,
234 234 headers={'Content-Type': 'application/mercurial-0.1'},
235 235 heads=' '.join(map(hex, heads)))
236 236 resp_code, output = resp.split('\n', 1)
237 237 try:
238 238 ret = int(resp_code)
239 239 except ValueError, err:
240 240 raise error.ResponseError(
241 241 _('push failed (unexpected response):'), resp)
242 self.ui.write(output)
242 for l in output.splitlines(True):
243 self.ui.status(_('remote: '), l)
243 244 return ret
244 245 except socket.error, err:
245 246 if err[0] in (errno.ECONNRESET, errno.EPIPE):
246 247 raise util.Abort(_('push failed: %s') % err[1])
247 248 raise util.Abort(err[1])
248 249 finally:
249 250 fp.close()
250 251 os.unlink(tempname)
251 252
252 253 def stream_out(self):
253 254 return self.do_cmd('stream_out')
254 255
255 256 class httpsrepository(httprepository):
256 257 def __init__(self, ui, path):
257 258 if not url.has_https:
258 259 raise util.Abort(_('Python support for SSL and HTTPS '
259 260 'is not installed'))
260 261 httprepository.__init__(self, ui, path)
261 262
262 263 def instance(ui, path, create):
263 264 if create:
264 265 raise util.Abort(_('cannot create new http repository'))
265 266 try:
266 267 if path.startswith('https:'):
267 268 inst = httpsrepository(ui, path)
268 269 else:
269 270 inst = httprepository(ui, path)
270 271 inst.between([(nullid, nullid)])
271 272 return inst
272 273 except error.RepoError:
273 274 ui.note('(falling back to static-http)\n')
274 275 return statichttprepo.instance(ui, "static-" + path, create)
@@ -1,38 +1,38 b''
1 1 adding a
2 2 updating to branch default
3 3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 4 % expect ssl error
5 5 pushing to http://localhost:$HGPORT/
6 6 searching for changes
7 ssl required
7 remote: ssl required
8 8 % serve errors
9 9 % expect authorization error
10 10 abort: authorization failed
11 11 pushing to http://localhost:$HGPORT/
12 12 searching for changes
13 13 % serve errors
14 14 % expect authorization error: must have authorized user
15 15 abort: authorization failed
16 16 pushing to http://localhost:$HGPORT/
17 17 searching for changes
18 18 % serve errors
19 19 % expect success
20 20 pushing to http://localhost:$HGPORT/
21 21 searching for changes
22 adding changesets
23 adding manifests
24 adding file changes
25 added 1 changesets with 1 changes to 1 files
22 remote: adding changesets
23 remote: adding manifests
24 remote: adding file changes
25 remote: added 1 changesets with 1 changes to 1 files
26 26 % serve errors
27 27 changegroup hook: HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_URL=remote:http
28 28 rolling back last transaction
29 29 % expect authorization error: all users denied
30 30 abort: authorization failed
31 31 pushing to http://localhost:$HGPORT/
32 32 searching for changes
33 33 % serve errors
34 34 % expect authorization error: some users denied, users must be authenticated
35 35 abort: authorization failed
36 36 pushing to http://localhost:$HGPORT/
37 37 searching for changes
38 38 % serve errors
General Comments 0
You need to be logged in to leave comments. Login now