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