comparison upmana/mercurial/httprepo.py @ 135:dcf4fbe09b70 beta

Traipse Beta 'OpenRPG' {091010-00} Traipse is a distribution of OpenRPG that is designed to be easy to setup and go. Traipse also makes it easy for developers to work on code without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy' and adds fixes to the code. 'Ornery-Orc's main goal is to offer more advanced features and enhance the productivity of the user. Update Summary (Beta) Added Bookmarks Fix to Remote Admin Commands Minor fix to text based Server Fix to Pretty Print, from Core Fix to Splitter Nodes not being created Fix to massive amounts of images loading, from Core Added 'boot' command to remote admin Added confirmation window for sent nodes Minor changes to allow for portability to an OpenSUSE linux OS Miniatures Layer pop up box allows users to turn off Mini labels, from FlexiRPG Zoom Mouse plugin added Images added to Plugin UI Switching to Element Tree Map efficiency, from FlexiRPG Added Status Bar to Update Manager default_manifest.xml renamed to default_upmana.xml Cleaner clode for saved repositories New TrueDebug Class in orpg_log (See documentation for usage) Mercurial's hgweb folder is ported to upmana **Pretty important update that can help remove thousands of dead children from your gametree. **Children, <forms />, <group_atts />, <horizontal />, <cols />, <rows />, <height />, etc... are all tags now. Check your gametree and look for dead children!! **New Gamtree Recusion method, mapping, and context sensitivity. !!Alpha - Watch out for infinite loops!!
author sirebral
date Tue, 10 Nov 2009 14:11:28 -0600
parents
children
comparison
equal deleted inserted replaced
101:394ebb3b6a0f 135:dcf4fbe09b70
1 # httprepo.py - HTTP repository proxy classes for mercurial
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2, incorporated herein by reference.
8
9 from node import bin, hex, nullid
10 from i18n import _
11 import repo, changegroup, statichttprepo, error, url, util
12 import os, urllib, urllib2, urlparse, zlib, httplib
13 import errno, socket
14
15 def zgenerator(f):
16 zd = zlib.decompressobj()
17 try:
18 for chunk in util.filechunkiter(f):
19 yield zd.decompress(chunk)
20 except httplib.HTTPException:
21 raise IOError(None, _('connection ended unexpectedly'))
22 yield zd.flush()
23
24 class httprepository(repo.repository):
25 def __init__(self, ui, path):
26 self.path = path
27 self.caps = None
28 self.handler = None
29 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
30 if query or frag:
31 raise util.Abort(_('unsupported URL component: "%s"') %
32 (query or frag))
33
34 # urllib cannot handle URLs with embedded user or passwd
35 self._url, authinfo = url.getauthinfo(path)
36
37 self.ui = ui
38 self.ui.debug(_('using %s\n') % self._url)
39
40 self.urlopener = url.opener(ui, authinfo)
41
42 def __del__(self):
43 for h in self.urlopener.handlers:
44 h.close()
45 if hasattr(h, "close_all"):
46 h.close_all()
47
48 def url(self):
49 return self.path
50
51 # look up capabilities only when needed
52
53 def get_caps(self):
54 if self.caps is None:
55 try:
56 self.caps = set(self.do_read('capabilities').split())
57 except error.RepoError:
58 self.caps = set()
59 self.ui.debug(_('capabilities: %s\n') %
60 (' '.join(self.caps or ['none'])))
61 return self.caps
62
63 capabilities = property(get_caps)
64
65 def lock(self):
66 raise util.Abort(_('operation not supported over http'))
67
68 def do_cmd(self, cmd, **args):
69 data = args.pop('data', None)
70 headers = args.pop('headers', {})
71 self.ui.debug(_("sending %s command\n") % cmd)
72 q = {"cmd": cmd}
73 q.update(args)
74 qs = '?%s' % urllib.urlencode(q)
75 cu = "%s%s" % (self._url, qs)
76 try:
77 if data:
78 self.ui.debug(_("sending %s bytes\n") % len(data))
79 resp = self.urlopener.open(urllib2.Request(cu, data, headers))
80 except urllib2.HTTPError, inst:
81 if inst.code == 401:
82 raise util.Abort(_('authorization failed'))
83 raise
84 except httplib.HTTPException, inst:
85 self.ui.debug(_('http error while sending %s command\n') % cmd)
86 self.ui.traceback()
87 raise IOError(None, inst)
88 except IndexError:
89 # this only happens with Python 2.3, later versions raise URLError
90 raise util.Abort(_('http error, possibly caused by proxy setting'))
91 # record the url we got redirected to
92 resp_url = resp.geturl()
93 if resp_url.endswith(qs):
94 resp_url = resp_url[:-len(qs)]
95 if self._url != resp_url:
96 self.ui.status(_('real URL is %s\n') % resp_url)
97 self._url = resp_url
98 try:
99 proto = resp.getheader('content-type')
100 except AttributeError:
101 proto = resp.headers['content-type']
102
103 safeurl = url.hidepassword(self._url)
104 # accept old "text/plain" and "application/hg-changegroup" for now
105 if not (proto.startswith('application/mercurial-') or
106 proto.startswith('text/plain') or
107 proto.startswith('application/hg-changegroup')):
108 self.ui.debug(_("requested URL: '%s'\n") % url.hidepassword(cu))
109 raise error.RepoError(_("'%s' does not appear to be an hg repository")
110 % safeurl)
111
112 if proto.startswith('application/mercurial-'):
113 try:
114 version = proto.split('-', 1)[1]
115 version_info = tuple([int(n) for n in version.split('.')])
116 except ValueError:
117 raise error.RepoError(_("'%s' sent a broken Content-Type "
118 "header (%s)") % (safeurl, proto))
119 if version_info > (0, 1):
120 raise error.RepoError(_("'%s' uses newer protocol %s") %
121 (safeurl, version))
122
123 return resp
124
125 def do_read(self, cmd, **args):
126 fp = self.do_cmd(cmd, **args)
127 try:
128 return fp.read()
129 finally:
130 # if using keepalive, allow connection to be reused
131 fp.close()
132
133 def lookup(self, key):
134 self.requirecap('lookup', _('look up remote revision'))
135 d = self.do_cmd("lookup", key = key).read()
136 success, data = d[:-1].split(' ', 1)
137 if int(success):
138 return bin(data)
139 raise error.RepoError(data)
140
141 def heads(self):
142 d = self.do_read("heads")
143 try:
144 return map(bin, d[:-1].split(" "))
145 except:
146 raise error.ResponseError(_("unexpected response:"), d)
147
148 def branchmap(self):
149 d = self.do_read("branchmap")
150 try:
151 branchmap = {}
152 for branchpart in d.splitlines():
153 branchheads = branchpart.split(' ')
154 branchname = urllib.unquote(branchheads[0])
155 branchheads = [bin(x) for x in branchheads[1:]]
156 branchmap[branchname] = branchheads
157 return branchmap
158 except:
159 raise error.ResponseError(_("unexpected response:"), d)
160
161 def branches(self, nodes):
162 n = " ".join(map(hex, nodes))
163 d = self.do_read("branches", nodes=n)
164 try:
165 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
166 return br
167 except:
168 raise error.ResponseError(_("unexpected response:"), d)
169
170 def between(self, pairs):
171 batch = 8 # avoid giant requests
172 r = []
173 for i in xrange(0, len(pairs), batch):
174 n = " ".join(["-".join(map(hex, p)) for p in pairs[i:i + batch]])
175 d = self.do_read("between", pairs=n)
176 try:
177 r += [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
178 except:
179 raise error.ResponseError(_("unexpected response:"), d)
180 return r
181
182 def changegroup(self, nodes, kind):
183 n = " ".join(map(hex, nodes))
184 f = self.do_cmd("changegroup", roots=n)
185 return util.chunkbuffer(zgenerator(f))
186
187 def changegroupsubset(self, bases, heads, source):
188 self.requirecap('changegroupsubset', _('look up remote changes'))
189 baselst = " ".join([hex(n) for n in bases])
190 headlst = " ".join([hex(n) for n in heads])
191 f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst)
192 return util.chunkbuffer(zgenerator(f))
193
194 def unbundle(self, cg, heads, source):
195 # have to stream bundle to a temp file because we do not have
196 # http 1.1 chunked transfer.
197
198 type = ""
199 types = self.capable('unbundle')
200 # servers older than d1b16a746db6 will send 'unbundle' as a
201 # boolean capability
202 try:
203 types = types.split(',')
204 except AttributeError:
205 types = [""]
206 if types:
207 for x in types:
208 if x in changegroup.bundletypes:
209 type = x
210 break
211
212 tempname = changegroup.writebundle(cg, None, type)
213 fp = url.httpsendfile(tempname, "rb")
214 try:
215 try:
216 resp = self.do_read(
217 'unbundle', data=fp,
218 headers={'Content-Type': 'application/octet-stream'},
219 heads=' '.join(map(hex, heads)))
220 resp_code, output = resp.split('\n', 1)
221 try:
222 ret = int(resp_code)
223 except ValueError, err:
224 raise error.ResponseError(
225 _('push failed (unexpected response):'), resp)
226 self.ui.write(output)
227 return ret
228 except socket.error, err:
229 if err[0] in (errno.ECONNRESET, errno.EPIPE):
230 raise util.Abort(_('push failed: %s') % err[1])
231 raise util.Abort(err[1])
232 finally:
233 fp.close()
234 os.unlink(tempname)
235
236 def stream_out(self):
237 return self.do_cmd('stream_out')
238
239 class httpsrepository(httprepository):
240 def __init__(self, ui, path):
241 if not url.has_https:
242 raise util.Abort(_('Python support for SSL and HTTPS '
243 'is not installed'))
244 httprepository.__init__(self, ui, path)
245
246 def instance(ui, path, create):
247 if create:
248 raise util.Abort(_('cannot create new http repository'))
249 try:
250 if path.startswith('https:'):
251 inst = httpsrepository(ui, path)
252 else:
253 inst = httprepository(ui, path)
254 inst.between([(nullid, nullid)])
255 return inst
256 except error.RepoError:
257 ui.note('(falling back to static-http)\n')
258 return statichttprepo.instance(ui, "static-" + path, create)