##// END OF EJS Templates
hgweb: in protocol adapter, avoid control reaching end of non-void function...
Augie Fackler -
r34510:e21f274c default
parent child Browse files
Show More
@@ -1,201 +1,203 b''
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 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import cgi
10 import cgi
11 import struct
11 import struct
12
12
13 from .common import (
13 from .common import (
14 HTTP_OK,
14 HTTP_OK,
15 )
15 )
16
16
17 from .. import (
17 from .. import (
18 error,
18 util,
19 util,
19 wireproto,
20 wireproto,
20 )
21 )
21 stringio = util.stringio
22 stringio = util.stringio
22
23
23 urlerr = util.urlerr
24 urlerr = util.urlerr
24 urlreq = util.urlreq
25 urlreq = util.urlreq
25
26
26 HGTYPE = 'application/mercurial-0.1'
27 HGTYPE = 'application/mercurial-0.1'
27 HGTYPE2 = 'application/mercurial-0.2'
28 HGTYPE2 = 'application/mercurial-0.2'
28 HGERRTYPE = 'application/hg-error'
29 HGERRTYPE = 'application/hg-error'
29
30
30 def decodevaluefromheaders(req, headerprefix):
31 def decodevaluefromheaders(req, headerprefix):
31 """Decode a long value from multiple HTTP request headers."""
32 """Decode a long value from multiple HTTP request headers."""
32 chunks = []
33 chunks = []
33 i = 1
34 i = 1
34 while True:
35 while True:
35 v = req.env.get('HTTP_%s_%d' % (
36 v = req.env.get('HTTP_%s_%d' % (
36 headerprefix.upper().replace('-', '_'), i))
37 headerprefix.upper().replace('-', '_'), i))
37 if v is None:
38 if v is None:
38 break
39 break
39 chunks.append(v)
40 chunks.append(v)
40 i += 1
41 i += 1
41
42
42 return ''.join(chunks)
43 return ''.join(chunks)
43
44
44 class webproto(wireproto.abstractserverproto):
45 class webproto(wireproto.abstractserverproto):
45 def __init__(self, req, ui):
46 def __init__(self, req, ui):
46 self.req = req
47 self.req = req
47 self.response = ''
48 self.response = ''
48 self.ui = ui
49 self.ui = ui
49 self.name = 'http'
50 self.name = 'http'
50
51
51 def getargs(self, args):
52 def getargs(self, args):
52 knownargs = self._args()
53 knownargs = self._args()
53 data = {}
54 data = {}
54 keys = args.split()
55 keys = args.split()
55 for k in keys:
56 for k in keys:
56 if k == '*':
57 if k == '*':
57 star = {}
58 star = {}
58 for key in knownargs.keys():
59 for key in knownargs.keys():
59 if key != 'cmd' and key not in keys:
60 if key != 'cmd' and key not in keys:
60 star[key] = knownargs[key][0]
61 star[key] = knownargs[key][0]
61 data['*'] = star
62 data['*'] = star
62 else:
63 else:
63 data[k] = knownargs[k][0]
64 data[k] = knownargs[k][0]
64 return [data[k] for k in keys]
65 return [data[k] for k in keys]
65 def _args(self):
66 def _args(self):
66 args = self.req.form.copy()
67 args = self.req.form.copy()
67 postlen = int(self.req.env.get('HTTP_X_HGARGS_POST', 0))
68 postlen = int(self.req.env.get('HTTP_X_HGARGS_POST', 0))
68 if postlen:
69 if postlen:
69 args.update(cgi.parse_qs(
70 args.update(cgi.parse_qs(
70 self.req.read(postlen), keep_blank_values=True))
71 self.req.read(postlen), keep_blank_values=True))
71 return args
72 return args
72
73
73 argvalue = decodevaluefromheaders(self.req, 'X-HgArg')
74 argvalue = decodevaluefromheaders(self.req, 'X-HgArg')
74 args.update(cgi.parse_qs(argvalue, keep_blank_values=True))
75 args.update(cgi.parse_qs(argvalue, keep_blank_values=True))
75 return args
76 return args
76 def getfile(self, fp):
77 def getfile(self, fp):
77 length = int(self.req.env['CONTENT_LENGTH'])
78 length = int(self.req.env['CONTENT_LENGTH'])
78 # If httppostargs is used, we need to read Content-Length
79 # If httppostargs is used, we need to read Content-Length
79 # minus the amount that was consumed by args.
80 # minus the amount that was consumed by args.
80 length -= int(self.req.env.get('HTTP_X_HGARGS_POST', 0))
81 length -= int(self.req.env.get('HTTP_X_HGARGS_POST', 0))
81 for s in util.filechunkiter(self.req, limit=length):
82 for s in util.filechunkiter(self.req, limit=length):
82 fp.write(s)
83 fp.write(s)
83 def redirect(self):
84 def redirect(self):
84 self.oldio = self.ui.fout, self.ui.ferr
85 self.oldio = self.ui.fout, self.ui.ferr
85 self.ui.ferr = self.ui.fout = stringio()
86 self.ui.ferr = self.ui.fout = stringio()
86 def restore(self):
87 def restore(self):
87 val = self.ui.fout.getvalue()
88 val = self.ui.fout.getvalue()
88 self.ui.ferr, self.ui.fout = self.oldio
89 self.ui.ferr, self.ui.fout = self.oldio
89 return val
90 return val
90
91
91 def _client(self):
92 def _client(self):
92 return 'remote:%s:%s:%s' % (
93 return 'remote:%s:%s:%s' % (
93 self.req.env.get('wsgi.url_scheme') or 'http',
94 self.req.env.get('wsgi.url_scheme') or 'http',
94 urlreq.quote(self.req.env.get('REMOTE_HOST', '')),
95 urlreq.quote(self.req.env.get('REMOTE_HOST', '')),
95 urlreq.quote(self.req.env.get('REMOTE_USER', '')))
96 urlreq.quote(self.req.env.get('REMOTE_USER', '')))
96
97
97 def responsetype(self, v1compressible=False):
98 def responsetype(self, v1compressible=False):
98 """Determine the appropriate response type and compression settings.
99 """Determine the appropriate response type and compression settings.
99
100
100 The ``v1compressible`` argument states whether the response with
101 The ``v1compressible`` argument states whether the response with
101 application/mercurial-0.1 media types should be zlib compressed.
102 application/mercurial-0.1 media types should be zlib compressed.
102
103
103 Returns a tuple of (mediatype, compengine, engineopts).
104 Returns a tuple of (mediatype, compengine, engineopts).
104 """
105 """
105 # For now, if it isn't compressible in the old world, it's never
106 # For now, if it isn't compressible in the old world, it's never
106 # compressible. We can change this to send uncompressed 0.2 payloads
107 # compressible. We can change this to send uncompressed 0.2 payloads
107 # later.
108 # later.
108 if not v1compressible:
109 if not v1compressible:
109 return HGTYPE, None, None
110 return HGTYPE, None, None
110
111
111 # Determine the response media type and compression engine based
112 # Determine the response media type and compression engine based
112 # on the request parameters.
113 # on the request parameters.
113 protocaps = decodevaluefromheaders(self.req, 'X-HgProto').split(' ')
114 protocaps = decodevaluefromheaders(self.req, 'X-HgProto').split(' ')
114
115
115 if '0.2' in protocaps:
116 if '0.2' in protocaps:
116 # Default as defined by wire protocol spec.
117 # Default as defined by wire protocol spec.
117 compformats = ['zlib', 'none']
118 compformats = ['zlib', 'none']
118 for cap in protocaps:
119 for cap in protocaps:
119 if cap.startswith('comp='):
120 if cap.startswith('comp='):
120 compformats = cap[5:].split(',')
121 compformats = cap[5:].split(',')
121 break
122 break
122
123
123 # Now find an agreed upon compression format.
124 # Now find an agreed upon compression format.
124 for engine in wireproto.supportedcompengines(self.ui, self,
125 for engine in wireproto.supportedcompengines(self.ui, self,
125 util.SERVERROLE):
126 util.SERVERROLE):
126 if engine.wireprotosupport().name in compformats:
127 if engine.wireprotosupport().name in compformats:
127 opts = {}
128 opts = {}
128 level = self.ui.configint('server',
129 level = self.ui.configint('server',
129 '%slevel' % engine.name())
130 '%slevel' % engine.name())
130 if level is not None:
131 if level is not None:
131 opts['level'] = level
132 opts['level'] = level
132
133
133 return HGTYPE2, engine, opts
134 return HGTYPE2, engine, opts
134
135
135 # No mutually supported compression format. Fall back to the
136 # No mutually supported compression format. Fall back to the
136 # legacy protocol.
137 # legacy protocol.
137
138
138 # Don't allow untrusted settings because disabling compression or
139 # Don't allow untrusted settings because disabling compression or
139 # setting a very high compression level could lead to flooding
140 # setting a very high compression level could lead to flooding
140 # the server's network or CPU.
141 # the server's network or CPU.
141 opts = {'level': self.ui.configint('server', 'zliblevel')}
142 opts = {'level': self.ui.configint('server', 'zliblevel')}
142 return HGTYPE, util.compengines['zlib'], opts
143 return HGTYPE, util.compengines['zlib'], opts
143
144
144 def iscmd(cmd):
145 def iscmd(cmd):
145 return cmd in wireproto.commands
146 return cmd in wireproto.commands
146
147
147 def call(repo, req, cmd):
148 def call(repo, req, cmd):
148 p = webproto(req, repo.ui)
149 p = webproto(req, repo.ui)
149
150
150 def genversion2(gen, compress, engine, engineopts):
151 def genversion2(gen, compress, engine, engineopts):
151 # application/mercurial-0.2 always sends a payload header
152 # application/mercurial-0.2 always sends a payload header
152 # identifying the compression engine.
153 # identifying the compression engine.
153 name = engine.wireprotosupport().name
154 name = engine.wireprotosupport().name
154 assert 0 < len(name) < 256
155 assert 0 < len(name) < 256
155 yield struct.pack('B', len(name))
156 yield struct.pack('B', len(name))
156 yield name
157 yield name
157
158
158 if compress:
159 if compress:
159 for chunk in engine.compressstream(gen, opts=engineopts):
160 for chunk in engine.compressstream(gen, opts=engineopts):
160 yield chunk
161 yield chunk
161 else:
162 else:
162 for chunk in gen:
163 for chunk in gen:
163 yield chunk
164 yield chunk
164
165
165 rsp = wireproto.dispatch(repo, p, cmd)
166 rsp = wireproto.dispatch(repo, p, cmd)
166 if isinstance(rsp, str):
167 if isinstance(rsp, str):
167 req.respond(HTTP_OK, HGTYPE, body=rsp)
168 req.respond(HTTP_OK, HGTYPE, body=rsp)
168 return []
169 return []
169 elif isinstance(rsp, wireproto.streamres):
170 elif isinstance(rsp, wireproto.streamres):
170 if rsp.reader:
171 if rsp.reader:
171 gen = iter(lambda: rsp.reader.read(32768), '')
172 gen = iter(lambda: rsp.reader.read(32768), '')
172 else:
173 else:
173 gen = rsp.gen
174 gen = rsp.gen
174
175
175 # This code for compression should not be streamres specific. It
176 # This code for compression should not be streamres specific. It
176 # is here because we only compress streamres at the moment.
177 # is here because we only compress streamres at the moment.
177 mediatype, engine, engineopts = p.responsetype(rsp.v1compressible)
178 mediatype, engine, engineopts = p.responsetype(rsp.v1compressible)
178
179
179 if mediatype == HGTYPE and rsp.v1compressible:
180 if mediatype == HGTYPE and rsp.v1compressible:
180 gen = engine.compressstream(gen, engineopts)
181 gen = engine.compressstream(gen, engineopts)
181 elif mediatype == HGTYPE2:
182 elif mediatype == HGTYPE2:
182 gen = genversion2(gen, rsp.v1compressible, engine, engineopts)
183 gen = genversion2(gen, rsp.v1compressible, engine, engineopts)
183
184
184 req.respond(HTTP_OK, mediatype)
185 req.respond(HTTP_OK, mediatype)
185 return gen
186 return gen
186 elif isinstance(rsp, wireproto.pushres):
187 elif isinstance(rsp, wireproto.pushres):
187 val = p.restore()
188 val = p.restore()
188 rsp = '%d\n%s' % (rsp.res, val)
189 rsp = '%d\n%s' % (rsp.res, val)
189 req.respond(HTTP_OK, HGTYPE, body=rsp)
190 req.respond(HTTP_OK, HGTYPE, body=rsp)
190 return []
191 return []
191 elif isinstance(rsp, wireproto.pusherr):
192 elif isinstance(rsp, wireproto.pusherr):
192 # drain the incoming bundle
193 # drain the incoming bundle
193 req.drain()
194 req.drain()
194 p.restore()
195 p.restore()
195 rsp = '0\n%s\n' % rsp.res
196 rsp = '0\n%s\n' % rsp.res
196 req.respond(HTTP_OK, HGTYPE, body=rsp)
197 req.respond(HTTP_OK, HGTYPE, body=rsp)
197 return []
198 return []
198 elif isinstance(rsp, wireproto.ooberror):
199 elif isinstance(rsp, wireproto.ooberror):
199 rsp = rsp.message
200 rsp = rsp.message
200 req.respond(HTTP_OK, HGERRTYPE, body=rsp)
201 req.respond(HTTP_OK, HGERRTYPE, body=rsp)
201 return []
202 return []
203 raise error.ProgrammingError('hgweb.protocol internal failure', rsp)
General Comments 0
You need to be logged in to leave comments. Login now