Skip to content

Query string ignored by FieldStorage class on POST if content-type is binary #16

Description

@zarquon5

Sorry to be the bearer of more bad news, but the following (new) FieldStorage test fails with w4py3 3.0.9:

    def testPostRequestWithBinaryDataAndQueryParams(self):
        content = b'\xfe\xff\xc0'
        with self.assertRaises(UnicodeDecodeError):
            content.decode('utf-8')
        length = len(content)
        fs = FieldStorage(fp=BytesIO(content), environ={
            'REQUEST_METHOD': 'POST',
            'CONTENT_LENGTH': length, 
            'CONTENT_TYPE': 'application/octet-stream',
            'QUERY_STRING': 'a=1&b=2&b=3&c=3'})
        self.assertEqual(fs.headers, {
            'content-type': 'application/octet-stream',
            'content-length': length})
        self.assertEqual(fs.type, 'application/octet-stream')
        self.assertEqual(fs.length, length)
        self.assertEqual(fs.bytes_read, length)
        self.assertEqual(fs.file.read(), content)
        self.assertEqual(fs.getfirst('a'), '1')    # this fails with a TypeError('not indexable')

That is, the query string is ignored if there is a non-empty binary payload in the post. The type of binary data doesn't matter - I just copied the non-utf8 case, since it was closest to my use case, to show the problem. (I ran into this when I tried to post a binary payload with some query parameters that indicate where to store that payload.)

In trying to diagnose this, I looked into the test:

    def testPostRequestWithQuery(self):
        fs = FieldStorage(fp=BytesIO(), environ={
            'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'a=1&b=2&b=3&c=3'})
        self.assertEqual(fs.getfirst('a'), '1')
        self.assertEqual(fs.getfirst('b'), '2')
        self.assertEqual(fs.getfirst('c'), '3')
        self.assertEqual(fs.getlist('a'), ['1'])
        self.assertEqual(fs.getlist('b'), ['2', '3'])
        self.assertEqual(fs.getlist('c'), ['3'])

to figure out why it passes, but my new one does not. Reading the code, AFAICT, if the headers are None, the content-type defaults to 'application/x-www-form-urlencoded', which triggers read_urlencoded(), which calls parse_qsl; this is also called within read_multi().

However, when the content-type is a binary type, read_single() is called, but that method does not call parse_qsl. (I couldn't determine whether/how the qs or qs_on_post gets/should get set for this path, so I am not sure what the correct code fix should be.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions