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