##// END OF EJS Templates
protocol: move hgweb protocol support back into protocol.py...
Matt Mackall -
r11595:368cd532 default
parent child Browse files
Show More
@@ -1,350 +1,285
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
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 import os, zlib, sys, cStringIO, urllib
9 import os, sys, urllib
10 from mercurial import ui, hg, hook, error, encoding, templater, wireproto, util
10 from mercurial import ui, hg, hook, error, encoding, templater, util
11 from common import get_mtime, ErrorResponse, permhooks
11 from common import get_mtime, ErrorResponse, permhooks
12 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
12 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
13 from request import wsgirequest
13 from request import wsgirequest
14 import webcommands, protocol, webutil
14 import webcommands, protocol, webutil
15
15
16 perms = {
16 perms = {
17 'changegroup': 'pull',
17 'changegroup': 'pull',
18 'changegroupsubset': 'pull',
18 'changegroupsubset': 'pull',
19 'stream_out': 'pull',
19 'stream_out': 'pull',
20 'listkeys': 'pull',
20 'listkeys': 'pull',
21 'unbundle': 'push',
21 'unbundle': 'push',
22 'pushkey': 'push',
22 'pushkey': 'push',
23 }
23 }
24
24
25 HGTYPE = 'application/mercurial-0.1'
26 class webproto(object):
27 def __init__(self, req):
28 self.req = req
29 self.response = ''
30 def getargs(self, args):
31 data = {}
32 keys = args.split()
33 for k in keys:
34 if k == '*':
35 star = {}
36 for key in self.req.form.keys():
37 if key not in keys:
38 star[key] = self.req.form[key][0]
39 data['*'] = star
40 else:
41 data[k] = self.req.form[k][0]
42 return [data[k] for k in keys]
43 def sendchangegroup(self, cg):
44 self.req.respond(HTTP_OK, HGTYPE)
45 z = zlib.compressobj()
46 while 1:
47 chunk = cg.read(4096)
48 if not chunk:
49 break
50 self.req.write(z.compress(chunk))
51 self.req.write(z.flush())
52 def sendstream(self, source):
53 self.req.respond(HTTP_OK, HGTYPE)
54 for chunk in source:
55 self.req.write(chunk)
56 def respond(self, s):
57 self.req.respond(HTTP_OK, HGTYPE, length=len(s))
58 self.response = s
59 def getfile(self, fp):
60 length = int(self.req.env['CONTENT_LENGTH'])
61 for s in util.filechunkiter(self.req, limit=length):
62 fp.write(s)
63 def redirect(self):
64 self.oldio = sys.stdout, sys.stderr
65 sys.stderr = sys.stdout = cStringIO.StringIO()
66 def respondpush(self, ret):
67 val = sys.stdout.getvalue()
68 sys.stdout, sys.stderr = self.oldio
69 self.req.respond(HTTP_OK, HGTYPE)
70 self.response = '%d\n%s' % (ret, val)
71 def _client(self):
72 return 'remote:%s:%s:%s' % (
73 self.req.env.get('wsgi.url_scheme') or 'http',
74 urllib.quote(self.req.env.get('REMOTE_HOST', '')),
75 urllib.quote(self.req.env.get('REMOTE_USER', '')))
76
77 def callproto(repo, req, cmd):
78 p = webproto(req)
79 r = wireproto.dispatch(repo, p, cmd)
80 yield p.response
81
82 class hgweb(object):
25 class hgweb(object):
83 def __init__(self, repo, name=None, baseui=None):
26 def __init__(self, repo, name=None, baseui=None):
84 if isinstance(repo, str):
27 if isinstance(repo, str):
85 if baseui:
28 if baseui:
86 u = baseui.copy()
29 u = baseui.copy()
87 else:
30 else:
88 u = ui.ui()
31 u = ui.ui()
89 self.repo = hg.repository(u, repo)
32 self.repo = hg.repository(u, repo)
90 else:
33 else:
91 self.repo = repo
34 self.repo = repo
92
35
93 self.repo.ui.setconfig('ui', 'report_untrusted', 'off')
36 self.repo.ui.setconfig('ui', 'report_untrusted', 'off')
94 self.repo.ui.setconfig('ui', 'interactive', 'off')
37 self.repo.ui.setconfig('ui', 'interactive', 'off')
95 hook.redirect(True)
38 hook.redirect(True)
96 self.mtime = -1
39 self.mtime = -1
97 self.reponame = name
40 self.reponame = name
98 self.archives = 'zip', 'gz', 'bz2'
41 self.archives = 'zip', 'gz', 'bz2'
99 self.stripecount = 1
42 self.stripecount = 1
100 # a repo owner may set web.templates in .hg/hgrc to get any file
43 # a repo owner may set web.templates in .hg/hgrc to get any file
101 # readable by the user running the CGI script
44 # readable by the user running the CGI script
102 self.templatepath = self.config('web', 'templates')
45 self.templatepath = self.config('web', 'templates')
103
46
104 # The CGI scripts are often run by a user different from the repo owner.
47 # The CGI scripts are often run by a user different from the repo owner.
105 # Trust the settings from the .hg/hgrc files by default.
48 # Trust the settings from the .hg/hgrc files by default.
106 def config(self, section, name, default=None, untrusted=True):
49 def config(self, section, name, default=None, untrusted=True):
107 return self.repo.ui.config(section, name, default,
50 return self.repo.ui.config(section, name, default,
108 untrusted=untrusted)
51 untrusted=untrusted)
109
52
110 def configbool(self, section, name, default=False, untrusted=True):
53 def configbool(self, section, name, default=False, untrusted=True):
111 return self.repo.ui.configbool(section, name, default,
54 return self.repo.ui.configbool(section, name, default,
112 untrusted=untrusted)
55 untrusted=untrusted)
113
56
114 def configlist(self, section, name, default=None, untrusted=True):
57 def configlist(self, section, name, default=None, untrusted=True):
115 return self.repo.ui.configlist(section, name, default,
58 return self.repo.ui.configlist(section, name, default,
116 untrusted=untrusted)
59 untrusted=untrusted)
117
60
118 def refresh(self, request=None):
61 def refresh(self, request=None):
119 if request:
62 if request:
120 self.repo.ui.environ = request.env
63 self.repo.ui.environ = request.env
121 mtime = get_mtime(self.repo.spath)
64 mtime = get_mtime(self.repo.spath)
122 if mtime != self.mtime:
65 if mtime != self.mtime:
123 self.mtime = mtime
66 self.mtime = mtime
124 self.repo = hg.repository(self.repo.ui, self.repo.root)
67 self.repo = hg.repository(self.repo.ui, self.repo.root)
125 self.maxchanges = int(self.config("web", "maxchanges", 10))
68 self.maxchanges = int(self.config("web", "maxchanges", 10))
126 self.stripecount = int(self.config("web", "stripes", 1))
69 self.stripecount = int(self.config("web", "stripes", 1))
127 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
70 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
128 self.maxfiles = int(self.config("web", "maxfiles", 10))
71 self.maxfiles = int(self.config("web", "maxfiles", 10))
129 self.allowpull = self.configbool("web", "allowpull", True)
72 self.allowpull = self.configbool("web", "allowpull", True)
130 encoding.encoding = self.config("web", "encoding",
73 encoding.encoding = self.config("web", "encoding",
131 encoding.encoding)
74 encoding.encoding)
132
75
133 def run(self):
76 def run(self):
134 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
77 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
135 raise RuntimeError("This function is only intended to be "
78 raise RuntimeError("This function is only intended to be "
136 "called while running as a CGI script.")
79 "called while running as a CGI script.")
137 import mercurial.hgweb.wsgicgi as wsgicgi
80 import mercurial.hgweb.wsgicgi as wsgicgi
138 wsgicgi.launch(self)
81 wsgicgi.launch(self)
139
82
140 def __call__(self, env, respond):
83 def __call__(self, env, respond):
141 req = wsgirequest(env, respond)
84 req = wsgirequest(env, respond)
142 return self.run_wsgi(req)
85 return self.run_wsgi(req)
143
86
144 def run_wsgi(self, req):
87 def run_wsgi(self, req):
145
88
146 self.refresh(req)
89 self.refresh(req)
147
90
148 # work with CGI variables to create coherent structure
91 # work with CGI variables to create coherent structure
149 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
92 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
150
93
151 req.url = req.env['SCRIPT_NAME']
94 req.url = req.env['SCRIPT_NAME']
152 if not req.url.endswith('/'):
95 if not req.url.endswith('/'):
153 req.url += '/'
96 req.url += '/'
154 if 'REPO_NAME' in req.env:
97 if 'REPO_NAME' in req.env:
155 req.url += req.env['REPO_NAME'] + '/'
98 req.url += req.env['REPO_NAME'] + '/'
156
99
157 if 'PATH_INFO' in req.env:
100 if 'PATH_INFO' in req.env:
158 parts = req.env['PATH_INFO'].strip('/').split('/')
101 parts = req.env['PATH_INFO'].strip('/').split('/')
159 repo_parts = req.env.get('REPO_NAME', '').split('/')
102 repo_parts = req.env.get('REPO_NAME', '').split('/')
160 if parts[:len(repo_parts)] == repo_parts:
103 if parts[:len(repo_parts)] == repo_parts:
161 parts = parts[len(repo_parts):]
104 parts = parts[len(repo_parts):]
162 query = '/'.join(parts)
105 query = '/'.join(parts)
163 else:
106 else:
164 query = req.env['QUERY_STRING'].split('&', 1)[0]
107 query = req.env['QUERY_STRING'].split('&', 1)[0]
165 query = query.split(';', 1)[0]
108 query = query.split(';', 1)[0]
166
109
167 # process this if it's a protocol request
110 # process this if it's a protocol request
168 # protocol bits don't need to create any URLs
111 # protocol bits don't need to create any URLs
169 # and the clients always use the old URL structure
112 # and the clients always use the old URL structure
170
113
171 cmd = req.form.get('cmd', [''])[0]
114 cmd = req.form.get('cmd', [''])[0]
172 if cmd and cmd in protocol.__all__:
115 if protocol.iscmd(cmd):
173 if query:
116 if query:
174 raise ErrorResponse(HTTP_NOT_FOUND)
117 raise ErrorResponse(HTTP_NOT_FOUND)
175 try:
118 if cmd in perms:
176 if cmd in perms:
119 try:
177 try:
120 self.check_perm(req, perms[cmd])
178 self.check_perm(req, perms[cmd])
121 except ErrorResponse, inst:
179 except ErrorResponse, inst:
122 if cmd == 'unbundle':
180 if cmd == 'unbundle':
123 req.drain()
181 req.drain()
124 req.respond(inst, protocol.HGTYPE)
182 raise
125 return '0\n%s\n' % inst.message
183 if cmd in wireproto.commands:
126 return protocol.call(self.repo, req, cmd)
184 return callproto(self.repo, req, cmd)
185 method = getattr(protocol, cmd)
186 return method(self.repo, req)
187 except ErrorResponse, inst:
188 req.respond(inst, protocol.HGTYPE)
189 if not inst.message:
190 return []
191 return '0\n%s\n' % inst.message,
192
127
193 # translate user-visible url structure to internal structure
128 # translate user-visible url structure to internal structure
194
129
195 args = query.split('/', 2)
130 args = query.split('/', 2)
196 if 'cmd' not in req.form and args and args[0]:
131 if 'cmd' not in req.form and args and args[0]:
197
132
198 cmd = args.pop(0)
133 cmd = args.pop(0)
199 style = cmd.rfind('-')
134 style = cmd.rfind('-')
200 if style != -1:
135 if style != -1:
201 req.form['style'] = [cmd[:style]]
136 req.form['style'] = [cmd[:style]]
202 cmd = cmd[style + 1:]
137 cmd = cmd[style + 1:]
203
138
204 # avoid accepting e.g. style parameter as command
139 # avoid accepting e.g. style parameter as command
205 if hasattr(webcommands, cmd):
140 if hasattr(webcommands, cmd):
206 req.form['cmd'] = [cmd]
141 req.form['cmd'] = [cmd]
207 else:
142 else:
208 cmd = ''
143 cmd = ''
209
144
210 if cmd == 'static':
145 if cmd == 'static':
211 req.form['file'] = ['/'.join(args)]
146 req.form['file'] = ['/'.join(args)]
212 else:
147 else:
213 if args and args[0]:
148 if args and args[0]:
214 node = args.pop(0)
149 node = args.pop(0)
215 req.form['node'] = [node]
150 req.form['node'] = [node]
216 if args:
151 if args:
217 req.form['file'] = args
152 req.form['file'] = args
218
153
219 ua = req.env.get('HTTP_USER_AGENT', '')
154 ua = req.env.get('HTTP_USER_AGENT', '')
220 if cmd == 'rev' and 'mercurial' in ua:
155 if cmd == 'rev' and 'mercurial' in ua:
221 req.form['style'] = ['raw']
156 req.form['style'] = ['raw']
222
157
223 if cmd == 'archive':
158 if cmd == 'archive':
224 fn = req.form['node'][0]
159 fn = req.form['node'][0]
225 for type_, spec in self.archive_specs.iteritems():
160 for type_, spec in self.archive_specs.iteritems():
226 ext = spec[2]
161 ext = spec[2]
227 if fn.endswith(ext):
162 if fn.endswith(ext):
228 req.form['node'] = [fn[:-len(ext)]]
163 req.form['node'] = [fn[:-len(ext)]]
229 req.form['type'] = [type_]
164 req.form['type'] = [type_]
230
165
231 # process the web interface request
166 # process the web interface request
232
167
233 try:
168 try:
234 tmpl = self.templater(req)
169 tmpl = self.templater(req)
235 ctype = tmpl('mimetype', encoding=encoding.encoding)
170 ctype = tmpl('mimetype', encoding=encoding.encoding)
236 ctype = templater.stringify(ctype)
171 ctype = templater.stringify(ctype)
237
172
238 # check read permissions non-static content
173 # check read permissions non-static content
239 if cmd != 'static':
174 if cmd != 'static':
240 self.check_perm(req, None)
175 self.check_perm(req, None)
241
176
242 if cmd == '':
177 if cmd == '':
243 req.form['cmd'] = [tmpl.cache['default']]
178 req.form['cmd'] = [tmpl.cache['default']]
244 cmd = req.form['cmd'][0]
179 cmd = req.form['cmd'][0]
245
180
246 if cmd not in webcommands.__all__:
181 if cmd not in webcommands.__all__:
247 msg = 'no such method: %s' % cmd
182 msg = 'no such method: %s' % cmd
248 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
183 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
249 elif cmd == 'file' and 'raw' in req.form.get('style', []):
184 elif cmd == 'file' and 'raw' in req.form.get('style', []):
250 self.ctype = ctype
185 self.ctype = ctype
251 content = webcommands.rawfile(self, req, tmpl)
186 content = webcommands.rawfile(self, req, tmpl)
252 else:
187 else:
253 content = getattr(webcommands, cmd)(self, req, tmpl)
188 content = getattr(webcommands, cmd)(self, req, tmpl)
254 req.respond(HTTP_OK, ctype)
189 req.respond(HTTP_OK, ctype)
255
190
256 return content
191 return content
257
192
258 except error.LookupError, err:
193 except error.LookupError, err:
259 req.respond(HTTP_NOT_FOUND, ctype)
194 req.respond(HTTP_NOT_FOUND, ctype)
260 msg = str(err)
195 msg = str(err)
261 if 'manifest' not in msg:
196 if 'manifest' not in msg:
262 msg = 'revision not found: %s' % err.name
197 msg = 'revision not found: %s' % err.name
263 return tmpl('error', error=msg)
198 return tmpl('error', error=msg)
264 except (error.RepoError, error.RevlogError), inst:
199 except (error.RepoError, error.RevlogError), inst:
265 req.respond(HTTP_SERVER_ERROR, ctype)
200 req.respond(HTTP_SERVER_ERROR, ctype)
266 return tmpl('error', error=str(inst))
201 return tmpl('error', error=str(inst))
267 except ErrorResponse, inst:
202 except ErrorResponse, inst:
268 req.respond(inst, ctype)
203 req.respond(inst, ctype)
269 return tmpl('error', error=inst.message)
204 return tmpl('error', error=inst.message)
270
205
271 def templater(self, req):
206 def templater(self, req):
272
207
273 # determine scheme, port and server name
208 # determine scheme, port and server name
274 # this is needed to create absolute urls
209 # this is needed to create absolute urls
275
210
276 proto = req.env.get('wsgi.url_scheme')
211 proto = req.env.get('wsgi.url_scheme')
277 if proto == 'https':
212 if proto == 'https':
278 proto = 'https'
213 proto = 'https'
279 default_port = "443"
214 default_port = "443"
280 else:
215 else:
281 proto = 'http'
216 proto = 'http'
282 default_port = "80"
217 default_port = "80"
283
218
284 port = req.env["SERVER_PORT"]
219 port = req.env["SERVER_PORT"]
285 port = port != default_port and (":" + port) or ""
220 port = port != default_port and (":" + port) or ""
286 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
221 urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
287 staticurl = self.config("web", "staticurl") or req.url + 'static/'
222 staticurl = self.config("web", "staticurl") or req.url + 'static/'
288 if not staticurl.endswith('/'):
223 if not staticurl.endswith('/'):
289 staticurl += '/'
224 staticurl += '/'
290
225
291 # some functions for the templater
226 # some functions for the templater
292
227
293 def header(**map):
228 def header(**map):
294 yield tmpl('header', encoding=encoding.encoding, **map)
229 yield tmpl('header', encoding=encoding.encoding, **map)
295
230
296 def footer(**map):
231 def footer(**map):
297 yield tmpl("footer", **map)
232 yield tmpl("footer", **map)
298
233
299 def motd(**map):
234 def motd(**map):
300 yield self.config("web", "motd", "")
235 yield self.config("web", "motd", "")
301
236
302 # figure out which style to use
237 # figure out which style to use
303
238
304 vars = {}
239 vars = {}
305 styles = (
240 styles = (
306 req.form.get('style', [None])[0],
241 req.form.get('style', [None])[0],
307 self.config('web', 'style'),
242 self.config('web', 'style'),
308 'paper',
243 'paper',
309 )
244 )
310 style, mapfile = templater.stylemap(styles, self.templatepath)
245 style, mapfile = templater.stylemap(styles, self.templatepath)
311 if style == styles[0]:
246 if style == styles[0]:
312 vars['style'] = style
247 vars['style'] = style
313
248
314 start = req.url[-1] == '?' and '&' or '?'
249 start = req.url[-1] == '?' and '&' or '?'
315 sessionvars = webutil.sessionvars(vars, start)
250 sessionvars = webutil.sessionvars(vars, start)
316
251
317 if not self.reponame:
252 if not self.reponame:
318 self.reponame = (self.config("web", "name")
253 self.reponame = (self.config("web", "name")
319 or req.env.get('REPO_NAME')
254 or req.env.get('REPO_NAME')
320 or req.url.strip('/') or self.repo.root)
255 or req.url.strip('/') or self.repo.root)
321
256
322 # create the templater
257 # create the templater
323
258
324 tmpl = templater.templater(mapfile,
259 tmpl = templater.templater(mapfile,
325 defaults={"url": req.url,
260 defaults={"url": req.url,
326 "staticurl": staticurl,
261 "staticurl": staticurl,
327 "urlbase": urlbase,
262 "urlbase": urlbase,
328 "repo": self.reponame,
263 "repo": self.reponame,
329 "header": header,
264 "header": header,
330 "footer": footer,
265 "footer": footer,
331 "motd": motd,
266 "motd": motd,
332 "sessionvars": sessionvars
267 "sessionvars": sessionvars
333 })
268 })
334 return tmpl
269 return tmpl
335
270
336 def archivelist(self, nodeid):
271 def archivelist(self, nodeid):
337 allowed = self.configlist("web", "allow_archive")
272 allowed = self.configlist("web", "allow_archive")
338 for i, spec in self.archive_specs.iteritems():
273 for i, spec in self.archive_specs.iteritems():
339 if i in allowed or self.configbool("web", "allow" + i):
274 if i in allowed or self.configbool("web", "allow" + i):
340 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
275 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
341
276
342 archive_specs = {
277 archive_specs = {
343 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
278 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
344 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
279 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
345 'zip': ('application/zip', 'zip', '.zip', None),
280 'zip': ('application/zip', 'zip', '.zip', None),
346 }
281 }
347
282
348 def check_perm(self, req, op):
283 def check_perm(self, req, op):
349 for hook in permhooks:
284 for hook in permhooks:
350 hook(self, req, op)
285 hook(self, req, op)
@@ -1,23 +1,71
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import cStringIO, zlib, tempfile, errno, os, sys, urllib, copy
8 import cStringIO, zlib, sys, urllib
9 from mercurial import util, streamclone, pushkey
9 from mercurial import util, wireproto
10 from mercurial.node import bin, hex
10 from common import HTTP_OK
11 from mercurial import changegroup as changegroupmod
12 from common import ErrorResponse, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
13
14 # __all__ is populated with the allowed commands. Be sure to add to it if
15 # you're adding a new command, or the new command won't work.
16
17 __all__ = [
18 'lookup', 'heads', 'branches', 'between', 'changegroup',
19 'changegroupsubset', 'capabilities', 'unbundle', 'stream_out',
20 'branchmap', 'pushkey', 'listkeys'
21 ]
22
11
23 HGTYPE = 'application/mercurial-0.1'
12 HGTYPE = 'application/mercurial-0.1'
13
14 class webproto(object):
15 def __init__(self, req):
16 self.req = req
17 self.response = ''
18 def getargs(self, args):
19 data = {}
20 keys = args.split()
21 for k in keys:
22 if k == '*':
23 star = {}
24 for key in self.req.form.keys():
25 if key not in keys:
26 star[key] = self.req.form[key][0]
27 data['*'] = star
28 else:
29 data[k] = self.req.form[k][0]
30 return [data[k] for k in keys]
31 def sendchangegroup(self, cg):
32 self.req.respond(HTTP_OK, HGTYPE)
33 z = zlib.compressobj()
34 while 1:
35 chunk = cg.read(4096)
36 if not chunk:
37 break
38 self.req.write(z.compress(chunk))
39 self.req.write(z.flush())
40 def sendstream(self, source):
41 self.req.respond(HTTP_OK, HGTYPE)
42 for chunk in source:
43 self.req.write(chunk)
44 def respond(self, s):
45 self.req.respond(HTTP_OK, HGTYPE, length=len(s))
46 self.response = s
47 def getfile(self, fp):
48 length = int(self.req.env['CONTENT_LENGTH'])
49 for s in util.filechunkiter(self.req, limit=length):
50 fp.write(s)
51 def redirect(self):
52 self.oldio = sys.stdout, sys.stderr
53 sys.stderr = sys.stdout = cStringIO.StringIO()
54 def respondpush(self, ret):
55 val = sys.stdout.getvalue()
56 sys.stdout, sys.stderr = self.oldio
57 self.req.respond(HTTP_OK, HGTYPE)
58 self.response = '%d\n%s' % (ret, val)
59 def _client(self):
60 return 'remote:%s:%s:%s' % (
61 self.req.env.get('wsgi.url_scheme') or 'http',
62 urllib.quote(self.req.env.get('REMOTE_HOST', '')),
63 urllib.quote(self.req.env.get('REMOTE_USER', '')))
64
65 def iscmd(cmd):
66 return cmd in wireproto.commands
67
68 def call(repo, req, cmd):
69 p = webproto(req)
70 r = wireproto.dispatch(repo, p, cmd)
71 yield p.response
General Comments 0
You need to be logged in to leave comments. Login now