Show More
@@ -28,6 +28,90 b' from .. import (' | |||
|
28 | 28 | util, |
|
29 | 29 | ) |
|
30 | 30 | |
|
31 | class multidict(object): | |
|
32 | """A dict like object that can store multiple values for a key. | |
|
33 | ||
|
34 | Used to store parsed request parameters. | |
|
35 | ||
|
36 | This is inspired by WebOb's class of the same name. | |
|
37 | """ | |
|
38 | def __init__(self): | |
|
39 | # Stores (key, value) 2-tuples. This isn't the most efficient. But we | |
|
40 | # don't rely on parameters that much, so it shouldn't be a perf issue. | |
|
41 | # we can always add dict for fast lookups. | |
|
42 | self._items = [] | |
|
43 | ||
|
44 | def __getitem__(self, key): | |
|
45 | """Returns the last set value for a key.""" | |
|
46 | for k, v in reversed(self._items): | |
|
47 | if k == key: | |
|
48 | return v | |
|
49 | ||
|
50 | raise KeyError(key) | |
|
51 | ||
|
52 | def __setitem__(self, key, value): | |
|
53 | """Replace a values for a key with a new value.""" | |
|
54 | try: | |
|
55 | del self[key] | |
|
56 | except KeyError: | |
|
57 | pass | |
|
58 | ||
|
59 | self._items.append((key, value)) | |
|
60 | ||
|
61 | def __delitem__(self, key): | |
|
62 | """Delete all values for a key.""" | |
|
63 | oldlen = len(self._items) | |
|
64 | ||
|
65 | self._items[:] = [(k, v) for k, v in self._items if k != key] | |
|
66 | ||
|
67 | if oldlen == len(self._items): | |
|
68 | raise KeyError(key) | |
|
69 | ||
|
70 | def __contains__(self, key): | |
|
71 | return any(k == key for k, v in self._items) | |
|
72 | ||
|
73 | def __len__(self): | |
|
74 | return len(self._items) | |
|
75 | ||
|
76 | def get(self, key, default=None): | |
|
77 | try: | |
|
78 | return self.__getitem__(key) | |
|
79 | except KeyError: | |
|
80 | return default | |
|
81 | ||
|
82 | def add(self, key, value): | |
|
83 | """Add a new value for a key. Does not replace existing values.""" | |
|
84 | self._items.append((key, value)) | |
|
85 | ||
|
86 | def getall(self, key): | |
|
87 | """Obtains all values for a key.""" | |
|
88 | return [v for k, v in self._items if k == key] | |
|
89 | ||
|
90 | def getone(self, key): | |
|
91 | """Obtain a single value for a key. | |
|
92 | ||
|
93 | Raises KeyError if key not defined or it has multiple values set. | |
|
94 | """ | |
|
95 | vals = self.getall(key) | |
|
96 | ||
|
97 | if not vals: | |
|
98 | raise KeyError(key) | |
|
99 | ||
|
100 | if len(vals) > 1: | |
|
101 | raise KeyError('multiple values for %r' % key) | |
|
102 | ||
|
103 | return vals[0] | |
|
104 | ||
|
105 | def asdictoflists(self): | |
|
106 | d = {} | |
|
107 | for k, v in self._items: | |
|
108 | if k in d: | |
|
109 | d[k].append(v) | |
|
110 | else: | |
|
111 | d[k] = [v] | |
|
112 | ||
|
113 | return d | |
|
114 | ||
|
31 | 115 | @attr.s(frozen=True) |
|
32 | 116 | class parsedrequest(object): |
|
33 | 117 | """Represents a parsed WSGI request. |
@@ -56,10 +140,8 b' class parsedrequest(object):' | |||
|
56 | 140 | havepathinfo = attr.ib() |
|
57 | 141 | # Raw query string (part after "?" in URL). |
|
58 | 142 | querystring = attr.ib() |
|
59 |
# |
|
|
60 |
q |
|
|
61 | # Dict of query string arguments. Values are lists with at least 1 item. | |
|
62 | querystringdict = attr.ib() | |
|
143 | # multidict of query string parameters. | |
|
144 | qsparams = attr.ib() | |
|
63 | 145 | # wsgiref.headers.Headers instance. Operates like a dict with case |
|
64 | 146 | # insensitive keys. |
|
65 | 147 | headers = attr.ib() |
@@ -157,14 +239,9 b' def parserequestfromenv(env, bodyfh):' | |||
|
157 | 239 | |
|
158 | 240 | # We store as a list so we have ordering information. We also store as |
|
159 | 241 | # a dict to facilitate fast lookup. |
|
160 | querystringlist = util.urlreq.parseqsl(querystring, keep_blank_values=True) | |
|
161 | ||
|
162 | querystringdict = {} | |
|
163 | for k, v in querystringlist: | |
|
164 | if k in querystringdict: | |
|
165 | querystringdict[k].append(v) | |
|
166 | else: | |
|
167 | querystringdict[k] = [v] | |
|
242 | qsparams = multidict() | |
|
243 | for k, v in util.urlreq.parseqsl(querystring, keep_blank_values=True): | |
|
244 | qsparams.add(k, v) | |
|
168 | 245 | |
|
169 | 246 | # HTTP_* keys contain HTTP request headers. The Headers structure should |
|
170 | 247 | # perform case normalization for us. We just rewrite underscore to dash |
@@ -197,8 +274,7 b' def parserequestfromenv(env, bodyfh):' | |||
|
197 | 274 | dispatchparts=dispatchparts, dispatchpath=dispatchpath, |
|
198 | 275 | havepathinfo='PATH_INFO' in env, |
|
199 | 276 | querystring=querystring, |
|
200 |
q |
|
|
201 | querystringdict=querystringdict, | |
|
277 | qsparams=qsparams, | |
|
202 | 278 | headers=headers, |
|
203 | 279 | bodyfh=bodyfh) |
|
204 | 280 | |
@@ -350,7 +426,7 b' class wsgirequest(object):' | |||
|
350 | 426 | self.run_once = wsgienv[r'wsgi.run_once'] |
|
351 | 427 | self.env = wsgienv |
|
352 | 428 | self.req = parserequestfromenv(wsgienv, inp) |
|
353 |
self.form = self.req.q |
|
|
429 | self.form = self.req.qsparams.asdictoflists() | |
|
354 | 430 | self.res = wsgiresponse(self.req, start_response) |
|
355 | 431 | self._start_response = start_response |
|
356 | 432 | self.server_write = None |
@@ -79,7 +79,7 b' class httpv1protocolhandler(wireprototyp' | |||
|
79 | 79 | return [data[k] for k in keys] |
|
80 | 80 | |
|
81 | 81 | def _args(self): |
|
82 | args = util.rapply(pycompat.bytesurl, self._wsgireq.form.copy()) | |
|
82 | args = self._req.qsparams.asdictoflists() | |
|
83 | 83 | postlen = int(self._req.headers.get(b'X-HgArgs-Post', 0)) |
|
84 | 84 | if postlen: |
|
85 | 85 | args.update(urlreq.parseqs( |
@@ -170,10 +170,10 b' def handlewsgirequest(rctx, wsgireq, req' | |||
|
170 | 170 | # HTTP version 1 wire protocol requests are denoted by a "cmd" query |
|
171 | 171 | # string parameter. If it isn't present, this isn't a wire protocol |
|
172 | 172 | # request. |
|
173 |
if 'cmd' not in req.q |
|
|
173 | if 'cmd' not in req.qsparams: | |
|
174 | 174 | return False |
|
175 | 175 | |
|
176 |
cmd = req.q |
|
|
176 | cmd = req.qsparams['cmd'] | |
|
177 | 177 | |
|
178 | 178 | # The "cmd" request parameter is used by both the wire protocol and hgweb. |
|
179 | 179 | # While not all wire protocol commands are available for all transports, |
General Comments 0
You need to be logged in to leave comments.
Login now