mirror of
https://github.com/python/cpython.git
synced 2025-08-01 15:43:13 +00:00
Now a from submitted via POST that also has a query string
will contain both FieldStorage and MiniFieldStorage items. Fixes #1817.
This commit is contained in:
parent
2da91c375b
commit
a6a4d50efe
4 changed files with 103 additions and 0 deletions
|
@ -165,6 +165,8 @@ data part of type :mimetype:`application/x-www-form-urlencoded`), the items will
|
|||
actually be instances of the class :class:`MiniFieldStorage`. In this case, the
|
||||
:attr:`list`, :attr:`file`, and :attr:`filename` attributes are always ``None``.
|
||||
|
||||
A form submitted via POST that also has a query string will contain both
|
||||
:class:`FieldStorage` and :class:`MiniFieldStorage` items.
|
||||
|
||||
Higher Level Interface
|
||||
----------------------
|
||||
|
|
11
Lib/cgi.py
11
Lib/cgi.py
|
@ -456,6 +456,7 @@ class FieldStorage:
|
|||
self.strict_parsing = strict_parsing
|
||||
if 'REQUEST_METHOD' in environ:
|
||||
method = environ['REQUEST_METHOD'].upper()
|
||||
self.qs_on_post = None
|
||||
if method == 'GET' or method == 'HEAD':
|
||||
if 'QUERY_STRING' in environ:
|
||||
qs = environ['QUERY_STRING']
|
||||
|
@ -474,6 +475,8 @@ class FieldStorage:
|
|||
headers['content-type'] = "application/x-www-form-urlencoded"
|
||||
if 'CONTENT_TYPE' in environ:
|
||||
headers['content-type'] = environ['CONTENT_TYPE']
|
||||
if 'QUERY_STRING' in environ:
|
||||
self.qs_on_post = environ['QUERY_STRING']
|
||||
if 'CONTENT_LENGTH' in environ:
|
||||
headers['content-length'] = environ['CONTENT_LENGTH']
|
||||
self.fp = fp or sys.stdin
|
||||
|
@ -631,6 +634,8 @@ class FieldStorage:
|
|||
def read_urlencoded(self):
|
||||
"""Internal: read data in query string format."""
|
||||
qs = self.fp.read(self.length)
|
||||
if self.qs_on_post:
|
||||
qs += '&' + self.qs_on_post
|
||||
self.list = list = []
|
||||
for key, value in parse_qsl(qs, self.keep_blank_values,
|
||||
self.strict_parsing):
|
||||
|
@ -645,6 +650,12 @@ class FieldStorage:
|
|||
if not valid_boundary(ib):
|
||||
raise ValueError, 'Invalid boundary in multipart form: %r' % (ib,)
|
||||
self.list = []
|
||||
if self.qs_on_post:
|
||||
for key, value in parse_qsl(self.qs_on_post, self.keep_blank_values,
|
||||
self.strict_parsing):
|
||||
self.list.append(MiniFieldStorage(key, value))
|
||||
FieldStorageClass = None
|
||||
|
||||
klass = self.FieldStorageClass or self.__class__
|
||||
part = klass(self.fp, {}, ib,
|
||||
environ, keep_blank_values, strict_parsing)
|
||||
|
|
|
@ -130,6 +130,17 @@ def first_elts(list):
|
|||
def first_second_elts(list):
|
||||
return map(lambda p:(p[0], p[1][0]), list)
|
||||
|
||||
def gen_result(data, environ):
|
||||
fake_stdin = StringIO(data)
|
||||
fake_stdin.seek(0)
|
||||
form = cgi.FieldStorage(fp=fake_stdin, environ=environ)
|
||||
|
||||
result = {}
|
||||
for k, v in dict(form).items():
|
||||
result[k] = type(v) is list and form.getlist(k) or v.value
|
||||
|
||||
return result
|
||||
|
||||
class CgiTests(unittest.TestCase):
|
||||
|
||||
def test_qsl(self):
|
||||
|
@ -278,6 +289,83 @@ Content-Disposition: form-data; name="submit"
|
|||
got = getattr(fs.list[x], k)
|
||||
self.assertEquals(got, exp)
|
||||
|
||||
_qs_result = {
|
||||
'key1': 'value1',
|
||||
'key2': ['value2x', 'value2y'],
|
||||
'key3': 'value3',
|
||||
'key4': 'value4'
|
||||
}
|
||||
def testQSAndUrlEncode(self):
|
||||
data = "key2=value2x&key3=value3&key4=value4"
|
||||
environ = {
|
||||
'CONTENT_LENGTH': str(len(data)),
|
||||
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
|
||||
'QUERY_STRING': 'key1=value1&key2=value2y',
|
||||
'REQUEST_METHOD': 'POST',
|
||||
}
|
||||
v = gen_result(data, environ)
|
||||
self.assertEqual(self._qs_result, v)
|
||||
|
||||
def testQSAndFormData(self):
|
||||
data = """
|
||||
---123
|
||||
Content-Disposition: form-data; name="key2"
|
||||
|
||||
value2y
|
||||
---123
|
||||
Content-Disposition: form-data; name="key3"
|
||||
|
||||
value3
|
||||
---123
|
||||
Content-Disposition: form-data; name="key4"
|
||||
|
||||
value4
|
||||
---123--
|
||||
"""
|
||||
environ = {
|
||||
'CONTENT_LENGTH': str(len(data)),
|
||||
'CONTENT_TYPE': 'multipart/form-data; boundary=-123',
|
||||
'QUERY_STRING': 'key1=value1&key2=value2x',
|
||||
'REQUEST_METHOD': 'POST',
|
||||
}
|
||||
v = gen_result(data, environ)
|
||||
self.assertEqual(self._qs_result, v)
|
||||
|
||||
def testQSAndFormDataFile(self):
|
||||
data = """
|
||||
---123
|
||||
Content-Disposition: form-data; name="key2"
|
||||
|
||||
value2y
|
||||
---123
|
||||
Content-Disposition: form-data; name="key3"
|
||||
|
||||
value3
|
||||
---123
|
||||
Content-Disposition: form-data; name="key4"
|
||||
|
||||
value4
|
||||
---123
|
||||
Content-Disposition: form-data; name="upload"; filename="fake.txt"
|
||||
Content-Type: text/plain
|
||||
|
||||
this is the content of the fake file
|
||||
|
||||
---123--
|
||||
"""
|
||||
environ = {
|
||||
'CONTENT_LENGTH': str(len(data)),
|
||||
'CONTENT_TYPE': 'multipart/form-data; boundary=-123',
|
||||
'QUERY_STRING': 'key1=value1&key2=value2x',
|
||||
'REQUEST_METHOD': 'POST',
|
||||
}
|
||||
result = self._qs_result.copy()
|
||||
result.update({
|
||||
'upload': 'this is the content of the fake file\n'
|
||||
})
|
||||
v = gen_result(data, environ)
|
||||
self.assertEqual(result, v)
|
||||
|
||||
def test_main():
|
||||
run_unittest(CgiTests)
|
||||
|
||||
|
|
|
@ -112,6 +112,8 @@ Library
|
|||
structures, to match the behaviour of 2.5 and 3.0 (now follows the common
|
||||
sense).
|
||||
|
||||
- Issue #1817: cgi now correctly handles the querystring on POST requests
|
||||
|
||||
- Issue #3136: fileConfig()'s disabling of old loggers is now conditional via
|
||||
an optional disable_existing_loggers parameter, but the default value is
|
||||
such that the old behaviour is preserved. Thanks to Leandro Lucarella for
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue