From 00bcae9874d5f0bbc7b883bc2e360bce0a2ee123 Mon Sep 17 00:00:00 2001 From: s3lph Date: Sat, 7 Jul 2018 15:29:32 +0200 Subject: [PATCH] Fixed behavior for missing Content-Type in multipart parts --- matemat/webserver/test/test_parse_request.py | 45 +++++++++++++------- matemat/webserver/util.py | 7 ++- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/matemat/webserver/test/test_parse_request.py b/matemat/webserver/test/test_parse_request.py index 0a94065..acb225c 100644 --- a/matemat/webserver/test/test_parse_request.py +++ b/matemat/webserver/test/test_parse_request.py @@ -185,6 +185,35 @@ class TestParseRequest(unittest.TestCase): self.assertEqual('', args['foo'].get_str()) self.assertEqual('42', args['bar'].get_str()) + def test_parse_post_multipart_no_contenttype(self): + """ + Test that the Content-Type is set to 'application/octet-stream' if it is absent from the multipart header. + """ + path, args = parse_args('/', + postbody=b'--testBoundary1337\r\n' + b'Content-Disposition: form-data; name="foo"\r\n' + b'Content-Type: text/plain\r\n\r\n' + b'42\r\n' + b'--testBoundary1337\r\n' + b'Content-Disposition: form-data; name="bar"; filename="bar.bin"\r\n' + b'Content-Type: application/octet-stream\r\n\r\n' + b'1337\r\n' + b'--testBoundary1337\r\n' + b'Content-Disposition: form-data; name="baz"\r\n\r\n' + b'Hello, World!\r\n' + b'--testBoundary1337--\r\n', + enctype='multipart/form-data; boundary=testBoundary1337') + self.assertEqual('/', path) + self.assertEqual(3, len(args)) + self.assertIn('foo', args) + self.assertIn('bar', args) + self.assertIn('baz', args) + self.assertTrue(args['foo'].is_scalar) + self.assertTrue(args['bar'].is_scalar) + self.assertTrue(args['baz'].is_scalar) + self.assertEqual('application/octet-stream', args['baz'].get_content_type()) + self.assertEqual('Hello, World!', args['baz'].get_str()) + def test_parse_post_multipart_broken_boundaries(self): with self.assertRaises(ValueError): # Boundary not defined in Content-Type @@ -237,22 +266,6 @@ class TestParseRequest(unittest.TestCase): b'Hello, World!\r\n' b'--testBoundary1337\r\n', enctype='multipart/form-data; boundary=testBoundary1337') - with self.assertRaises(ValueError): - # Missing Content-Type header in one part - parse_args('/', - postbody=b'--testBoundary1337\r\n' - b'Content-Disposition: form-data; name="foo"\r\n' - b'Content-Type: text/plain\r\n\r\n' - b'42\r\n' - b'--testBoundary1337\r\n' - b'Content-Disposition: form-data; name="bar"; filename="bar.bin"\r\n' - b'Content-Type: application/octet-stream\r\n\r\n' - b'1337\r\n' - b'--testBoundary1337\r\n' - b'Content-Disposition: form-data; name="baz"\r\n\r\n' - b'Hello, World!\r\n' - b'--testBoundary1337--\r\n', - enctype='multipart/form-data; boundary=testBoundary1337') with self.assertRaises(ValueError): # Missing Content-Disposition header in one part parse_args('/', diff --git a/matemat/webserver/util.py b/matemat/webserver/util.py index 2bc2244..6e19d7b 100644 --- a/matemat/webserver/util.py +++ b/matemat/webserver/util.py @@ -46,8 +46,11 @@ def _parse_multipart(body: bytes, boundary: str) -> List[RequestArgument]: # Add header to hdr dict hk, hv = head.decode('utf-8').split(':') hdr[hk.strip()] = hv.strip() - # At least Content-Type and Content-Disposition must be present - if 'Content-Type' not in hdr or 'Content-Disposition' not in hdr: + # No content type set - set broadest possible type + if 'Content-Type' not in hdr: + hdr['Content-Type'] = 'application/octet-stream' + # At least Content-Disposition must be present + if 'Content-Disposition' not in hdr: raise ValueError('Missing Content-Type or Content-Disposition header') # Extract Content-Disposition header value and its arguments cd, *cdargs = hdr['Content-Disposition'].split(';')