##// END OF EJS Templates
httprepo: lowercase debug output
Martin Geisler -
r7923:016d6357 default
parent child Browse files
Show More
@@ -1,243 +1,243 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 bin, hex, nullid
9 from node import bin, hex, nullid
10 from i18n import _
10 from i18n import _
11 import repo, os, urllib, urllib2, urlparse, zlib, util, httplib
11 import repo, os, urllib, urllib2, urlparse, zlib, util, httplib
12 import errno, socket, changegroup, statichttprepo, error, url
12 import errno, socket, changegroup, statichttprepo, error, url
13
13
14 def zgenerator(f):
14 def zgenerator(f):
15 zd = zlib.decompressobj()
15 zd = zlib.decompressobj()
16 try:
16 try:
17 for chunk in util.filechunkiter(f):
17 for chunk in util.filechunkiter(f):
18 yield zd.decompress(chunk)
18 yield zd.decompress(chunk)
19 except httplib.HTTPException:
19 except httplib.HTTPException:
20 raise IOError(None, _('connection ended unexpectedly'))
20 raise IOError(None, _('connection ended unexpectedly'))
21 yield zd.flush()
21 yield zd.flush()
22
22
23 class httprepository(repo.repository):
23 class httprepository(repo.repository):
24 def __init__(self, ui, path):
24 def __init__(self, ui, path):
25 self.path = path
25 self.path = path
26 self.caps = None
26 self.caps = None
27 self.handler = None
27 self.handler = None
28 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
28 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
29 if query or frag:
29 if query or frag:
30 raise util.Abort(_('unsupported URL component: "%s"') %
30 raise util.Abort(_('unsupported URL component: "%s"') %
31 (query or frag))
31 (query or frag))
32
32
33 # urllib cannot handle URLs with embedded user or passwd
33 # urllib cannot handle URLs with embedded user or passwd
34 self._url, authinfo = url.getauthinfo(path)
34 self._url, authinfo = url.getauthinfo(path)
35
35
36 self.ui = ui
36 self.ui = ui
37 self.ui.debug(_('using %s\n') % self._url)
37 self.ui.debug(_('using %s\n') % self._url)
38
38
39 self.urlopener = url.opener(ui, authinfo)
39 self.urlopener = url.opener(ui, authinfo)
40
40
41 def __del__(self):
41 def __del__(self):
42 for h in self.urlopener.handlers:
42 for h in self.urlopener.handlers:
43 h.close()
43 h.close()
44 if hasattr(h, "close_all"):
44 if hasattr(h, "close_all"):
45 h.close_all()
45 h.close_all()
46
46
47 def url(self):
47 def url(self):
48 return self.path
48 return self.path
49
49
50 # look up capabilities only when needed
50 # look up capabilities only when needed
51
51
52 def get_caps(self):
52 def get_caps(self):
53 if self.caps is None:
53 if self.caps is None:
54 try:
54 try:
55 self.caps = util.set(self.do_read('capabilities').split())
55 self.caps = util.set(self.do_read('capabilities').split())
56 except error.RepoError:
56 except error.RepoError:
57 self.caps = util.set()
57 self.caps = util.set()
58 self.ui.debug(_('capabilities: %s\n') %
58 self.ui.debug(_('capabilities: %s\n') %
59 (' '.join(self.caps or ['none'])))
59 (' '.join(self.caps or ['none'])))
60 return self.caps
60 return self.caps
61
61
62 capabilities = property(get_caps)
62 capabilities = property(get_caps)
63
63
64 def lock(self):
64 def lock(self):
65 raise util.Abort(_('operation not supported over http'))
65 raise util.Abort(_('operation not supported over http'))
66
66
67 def do_cmd(self, cmd, **args):
67 def do_cmd(self, cmd, **args):
68 data = args.pop('data', None)
68 data = args.pop('data', None)
69 headers = args.pop('headers', {})
69 headers = args.pop('headers', {})
70 self.ui.debug(_("sending %s command\n") % cmd)
70 self.ui.debug(_("sending %s command\n") % cmd)
71 q = {"cmd": cmd}
71 q = {"cmd": cmd}
72 q.update(args)
72 q.update(args)
73 qs = '?%s' % urllib.urlencode(q)
73 qs = '?%s' % urllib.urlencode(q)
74 cu = "%s%s" % (self._url, qs)
74 cu = "%s%s" % (self._url, qs)
75 try:
75 try:
76 if data:
76 if data:
77 self.ui.debug(_("sending %s bytes\n") % len(data))
77 self.ui.debug(_("sending %s bytes\n") % len(data))
78 resp = self.urlopener.open(urllib2.Request(cu, data, headers))
78 resp = self.urlopener.open(urllib2.Request(cu, data, headers))
79 except urllib2.HTTPError, inst:
79 except urllib2.HTTPError, inst:
80 if inst.code == 401:
80 if inst.code == 401:
81 raise util.Abort(_('authorization failed'))
81 raise util.Abort(_('authorization failed'))
82 raise
82 raise
83 except httplib.HTTPException, inst:
83 except httplib.HTTPException, inst:
84 self.ui.debug(_('http error while sending %s command\n') % cmd)
84 self.ui.debug(_('http error while sending %s command\n') % cmd)
85 self.ui.print_exc()
85 self.ui.print_exc()
86 raise IOError(None, inst)
86 raise IOError(None, inst)
87 except IndexError:
87 except IndexError:
88 # this only happens with Python 2.3, later versions raise URLError
88 # this only happens with Python 2.3, later versions raise URLError
89 raise util.Abort(_('http error, possibly caused by proxy setting'))
89 raise util.Abort(_('http error, possibly caused by proxy setting'))
90 # record the url we got redirected to
90 # record the url we got redirected to
91 resp_url = resp.geturl()
91 resp_url = resp.geturl()
92 if resp_url.endswith(qs):
92 if resp_url.endswith(qs):
93 resp_url = resp_url[:-len(qs)]
93 resp_url = resp_url[:-len(qs)]
94 if self._url != resp_url:
94 if self._url != resp_url:
95 self.ui.status(_('real URL is %s\n') % resp_url)
95 self.ui.status(_('real URL is %s\n') % resp_url)
96 self._url = resp_url
96 self._url = resp_url
97 try:
97 try:
98 proto = resp.getheader('content-type')
98 proto = resp.getheader('content-type')
99 except AttributeError:
99 except AttributeError:
100 proto = resp.headers['content-type']
100 proto = resp.headers['content-type']
101
101
102 # accept old "text/plain" and "application/hg-changegroup" for now
102 # accept old "text/plain" and "application/hg-changegroup" for now
103 if not (proto.startswith('application/mercurial-') or
103 if not (proto.startswith('application/mercurial-') or
104 proto.startswith('text/plain') or
104 proto.startswith('text/plain') or
105 proto.startswith('application/hg-changegroup')):
105 proto.startswith('application/hg-changegroup')):
106 self.ui.debug(_("Requested URL: '%s'\n") % cu)
106 self.ui.debug(_("requested URL: '%s'\n") % cu)
107 raise error.RepoError(_("'%s' does not appear to be an hg repository")
107 raise error.RepoError(_("'%s' does not appear to be an hg repository")
108 % self._url)
108 % self._url)
109
109
110 if proto.startswith('application/mercurial-'):
110 if proto.startswith('application/mercurial-'):
111 try:
111 try:
112 version = proto.split('-', 1)[1]
112 version = proto.split('-', 1)[1]
113 version_info = tuple([int(n) for n in version.split('.')])
113 version_info = tuple([int(n) for n in version.split('.')])
114 except ValueError:
114 except ValueError:
115 raise error.RepoError(_("'%s' sent a broken Content-Type "
115 raise error.RepoError(_("'%s' sent a broken Content-Type "
116 "header (%s)") % (self._url, proto))
116 "header (%s)") % (self._url, proto))
117 if version_info > (0, 1):
117 if version_info > (0, 1):
118 raise error.RepoError(_("'%s' uses newer protocol %s") %
118 raise error.RepoError(_("'%s' uses newer protocol %s") %
119 (self._url, version))
119 (self._url, version))
120
120
121 return resp
121 return resp
122
122
123 def do_read(self, cmd, **args):
123 def do_read(self, cmd, **args):
124 fp = self.do_cmd(cmd, **args)
124 fp = self.do_cmd(cmd, **args)
125 try:
125 try:
126 return fp.read()
126 return fp.read()
127 finally:
127 finally:
128 # if using keepalive, allow connection to be reused
128 # if using keepalive, allow connection to be reused
129 fp.close()
129 fp.close()
130
130
131 def lookup(self, key):
131 def lookup(self, key):
132 self.requirecap('lookup', _('look up remote revision'))
132 self.requirecap('lookup', _('look up remote revision'))
133 d = self.do_cmd("lookup", key = key).read()
133 d = self.do_cmd("lookup", key = key).read()
134 success, data = d[:-1].split(' ', 1)
134 success, data = d[:-1].split(' ', 1)
135 if int(success):
135 if int(success):
136 return bin(data)
136 return bin(data)
137 raise error.RepoError(data)
137 raise error.RepoError(data)
138
138
139 def heads(self):
139 def heads(self):
140 d = self.do_read("heads")
140 d = self.do_read("heads")
141 try:
141 try:
142 return map(bin, d[:-1].split(" "))
142 return map(bin, d[:-1].split(" "))
143 except:
143 except:
144 raise error.ResponseError(_("unexpected response:"), d)
144 raise error.ResponseError(_("unexpected response:"), d)
145
145
146 def branches(self, nodes):
146 def branches(self, nodes):
147 n = " ".join(map(hex, nodes))
147 n = " ".join(map(hex, nodes))
148 d = self.do_read("branches", nodes=n)
148 d = self.do_read("branches", nodes=n)
149 try:
149 try:
150 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
150 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
151 return br
151 return br
152 except:
152 except:
153 raise error.ResponseError(_("unexpected response:"), d)
153 raise error.ResponseError(_("unexpected response:"), d)
154
154
155 def between(self, pairs):
155 def between(self, pairs):
156 batch = 8 # avoid giant requests
156 batch = 8 # avoid giant requests
157 r = []
157 r = []
158 for i in xrange(0, len(pairs), batch):
158 for i in xrange(0, len(pairs), batch):
159 n = " ".join(["-".join(map(hex, p)) for p in pairs[i:i + batch]])
159 n = " ".join(["-".join(map(hex, p)) for p in pairs[i:i + batch]])
160 d = self.do_read("between", pairs=n)
160 d = self.do_read("between", pairs=n)
161 try:
161 try:
162 r += [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
162 r += [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
163 except:
163 except:
164 raise error.ResponseError(_("unexpected response:"), d)
164 raise error.ResponseError(_("unexpected response:"), d)
165 return r
165 return r
166
166
167 def changegroup(self, nodes, kind):
167 def changegroup(self, nodes, kind):
168 n = " ".join(map(hex, nodes))
168 n = " ".join(map(hex, nodes))
169 f = self.do_cmd("changegroup", roots=n)
169 f = self.do_cmd("changegroup", roots=n)
170 return util.chunkbuffer(zgenerator(f))
170 return util.chunkbuffer(zgenerator(f))
171
171
172 def changegroupsubset(self, bases, heads, source):
172 def changegroupsubset(self, bases, heads, source):
173 self.requirecap('changegroupsubset', _('look up remote changes'))
173 self.requirecap('changegroupsubset', _('look up remote changes'))
174 baselst = " ".join([hex(n) for n in bases])
174 baselst = " ".join([hex(n) for n in bases])
175 headlst = " ".join([hex(n) for n in heads])
175 headlst = " ".join([hex(n) for n in heads])
176 f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst)
176 f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst)
177 return util.chunkbuffer(zgenerator(f))
177 return util.chunkbuffer(zgenerator(f))
178
178
179 def unbundle(self, cg, heads, source):
179 def unbundle(self, cg, heads, source):
180 # have to stream bundle to a temp file because we do not have
180 # have to stream bundle to a temp file because we do not have
181 # http 1.1 chunked transfer.
181 # http 1.1 chunked transfer.
182
182
183 type = ""
183 type = ""
184 types = self.capable('unbundle')
184 types = self.capable('unbundle')
185 # servers older than d1b16a746db6 will send 'unbundle' as a
185 # servers older than d1b16a746db6 will send 'unbundle' as a
186 # boolean capability
186 # boolean capability
187 try:
187 try:
188 types = types.split(',')
188 types = types.split(',')
189 except AttributeError:
189 except AttributeError:
190 types = [""]
190 types = [""]
191 if types:
191 if types:
192 for x in types:
192 for x in types:
193 if x in changegroup.bundletypes:
193 if x in changegroup.bundletypes:
194 type = x
194 type = x
195 break
195 break
196
196
197 tempname = changegroup.writebundle(cg, None, type)
197 tempname = changegroup.writebundle(cg, None, type)
198 fp = url.httpsendfile(tempname, "rb")
198 fp = url.httpsendfile(tempname, "rb")
199 try:
199 try:
200 try:
200 try:
201 resp = self.do_read(
201 resp = self.do_read(
202 'unbundle', data=fp,
202 'unbundle', data=fp,
203 headers={'Content-Type': 'application/octet-stream'},
203 headers={'Content-Type': 'application/octet-stream'},
204 heads=' '.join(map(hex, heads)))
204 heads=' '.join(map(hex, heads)))
205 resp_code, output = resp.split('\n', 1)
205 resp_code, output = resp.split('\n', 1)
206 try:
206 try:
207 ret = int(resp_code)
207 ret = int(resp_code)
208 except ValueError, err:
208 except ValueError, err:
209 raise error.ResponseError(
209 raise error.ResponseError(
210 _('push failed (unexpected response):'), resp)
210 _('push failed (unexpected response):'), resp)
211 self.ui.write(output)
211 self.ui.write(output)
212 return ret
212 return ret
213 except socket.error, err:
213 except socket.error, err:
214 if err[0] in (errno.ECONNRESET, errno.EPIPE):
214 if err[0] in (errno.ECONNRESET, errno.EPIPE):
215 raise util.Abort(_('push failed: %s') % err[1])
215 raise util.Abort(_('push failed: %s') % err[1])
216 raise util.Abort(err[1])
216 raise util.Abort(err[1])
217 finally:
217 finally:
218 fp.close()
218 fp.close()
219 os.unlink(tempname)
219 os.unlink(tempname)
220
220
221 def stream_out(self):
221 def stream_out(self):
222 return self.do_cmd('stream_out')
222 return self.do_cmd('stream_out')
223
223
224 class httpsrepository(httprepository):
224 class httpsrepository(httprepository):
225 def __init__(self, ui, path):
225 def __init__(self, ui, path):
226 if not url.has_https:
226 if not url.has_https:
227 raise util.Abort(_('Python support for SSL and HTTPS '
227 raise util.Abort(_('Python support for SSL and HTTPS '
228 'is not installed'))
228 'is not installed'))
229 httprepository.__init__(self, ui, path)
229 httprepository.__init__(self, ui, path)
230
230
231 def instance(ui, path, create):
231 def instance(ui, path, create):
232 if create:
232 if create:
233 raise util.Abort(_('cannot create new http repository'))
233 raise util.Abort(_('cannot create new http repository'))
234 try:
234 try:
235 if path.startswith('https:'):
235 if path.startswith('https:'):
236 inst = httpsrepository(ui, path)
236 inst = httpsrepository(ui, path)
237 else:
237 else:
238 inst = httprepository(ui, path)
238 inst = httprepository(ui, path)
239 inst.between([(nullid, nullid)])
239 inst.between([(nullid, nullid)])
240 return inst
240 return inst
241 except error.RepoError:
241 except error.RepoError:
242 ui.note('(falling back to static-http)\n')
242 ui.note('(falling back to static-http)\n')
243 return statichttprepo.instance(ui, "static-" + path, create)
243 return statichttprepo.instance(ui, "static-" + path, create)
General Comments 0
You need to be logged in to leave comments. Login now