def test_empty_input(): handler = media.MultipartFormHandler() form = handler.deserialize(io.BytesIO(), 'multipart/form-data; boundary=404', 0) with pytest.raises(falcon.HTTPBadRequest): for part in form: pass
def test_filename_star(): # NOTE(vytas): Generated by requests_toolbelt 0.9.1 on Py2 # (interestingly, one gets a "normal" filename on Py3.7). data = ( b'--a0d738bcdb30449eb0d13f4b72c2897e\r\n' b'Content-Disposition: form-data; name="file"; ' b"filename*=utf-8''%E2%AC%85%20Arrow.txt\r\n\r\n" b'A unicode arrow in the filename.\r\n' b'--a0d738bcdb30449eb0d13f4b72c2897e--\r\n' ) handler = media.MultipartFormHandler() content_type = ('multipart/form-data; boundary=' + 'a0d738bcdb30449eb0d13f4b72c2897e') stream = BufferedReader(io.BytesIO(data).read, len(data)) form = handler.deserialize(stream, content_type, len(data)) for part in form: assert part.filename == '⬅ Arrow.txt' assert part.secure_filename == '__Arrow.txt' data = data.replace(b'*=utf-8', b'*=esoteric') stream = BufferedReader(io.BytesIO(data).read, len(data)) form = handler.deserialize(stream, content_type, len(data)) for part in form: with pytest.raises(falcon.HTTPBadRequest): part.filename
def _factory(options): multipart_handler = media.MultipartFormHandler() for key, value in options.items(): setattr(multipart_handler.parse_options, key, value) req_handlers = media.Handlers({ falcon.MEDIA_JSON: media.JSONHandler(), falcon.MEDIA_MULTIPART: multipart_handler, }) app = create_app(asgi) app.req_options.media_handlers = req_handlers app.resp_options.media_handlers = media.Handlers({ falcon.MEDIA_JSON: media.JSONHandler(), falcon.MEDIA_MSGPACK: media.MessagePackHandler(), }) resource = AsyncMultipartAnalyzer() if asgi else MultipartAnalyzer() app.add_route('/submit', resource) app.add_route('/media', resource, suffix='media') app.add_route('/mirror', resource, suffix='mirror') return testing.TestClient(app)
async def test_async_unsupported(): if falcon.ASGI_SUPPORTED: pytest.skip('Testing missing ASGI support') handler = media.MultipartFormHandler() with pytest.raises(NotImplementedError): await handler.deserialize_async( 'Dummy', 'multipart/form-data; boundary=BOUNDARY', None)
def test_nested_multipart_mixed(): class Forms: def on_post(self, req, resp): example = {} for part in req.media: if part.content_type.startswith('multipart/mixed'): for nested in part.media: example[nested.filename] = nested.text resp.media = example parser = media.MultipartFormHandler() parser.parse_options.media_handlers['multipart/mixed'] = ( media.MultipartFormHandler()) app = falcon.App() app.req_options.media_handlers[falcon.MEDIA_MULTIPART] = parser app.add_route('/forms', Forms()) client = testing.TestClient(app) form_data = ( b'--AaB03x\r\n' b'Content-Disposition: form-data; name="field1"\r\n\r\n' b'Joe Blow\r\n' b'--AaB03x\r\n' b'Content-Disposition: form-data; name="docs"\r\n' b'Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n' b'--BbC04y\r\n' b'Content-Disposition: attachment; filename="file1.txt"\r\n\r\n' b'This is file1.\r\n\r\n' b'--BbC04y\r\n' b'Content-Disposition: attachment; filename="file2.txt"\r\n' b'Content-Transfer-Encoding: binary\r\n\r\n' b'Hello, World!\r\n\r\n' b'--BbC04y--\r\n\r\n' b'--AaB03x--\r\n') resp = client.simulate_post( '/forms', headers={'Content-Type': 'multipart/form-data; boundary=AaB03x'}, body=form_data, ) assert resp.status_code == 200 assert resp.json == { 'file1.txt': 'This is file1.\r\n', 'file2.txt': 'Hello, World!\r\n', }
def test_parse(boundary): handler = media.MultipartFormHandler() example = EXAMPLES[boundary] form = handler.deserialize(io.BytesIO(example), 'multipart/form-data; boundary=' + boundary, len(example)) for part in form: output = io.BytesIO() part.stream.pipe(output) assert isinstance(output.getvalue(), bytes)
def test_body_part_media(): handler = media.MultipartFormHandler() content_type = 'multipart/form-data; boundary=' + '5b11af82ab65407ba8cdccf37d2a9c4f' form = handler.deserialize(io.BytesIO(EXAMPLE1), content_type, len(EXAMPLE1)) expected = {'debug': True, 'message': 'Hello, world!', 'score': 7} for part in form: if part.content_type == 'application/json': assert part.media == part.media == expected
def test_missing_boundary(): handler = media.MultipartFormHandler() with pytest.raises(falcon.HTTPInvalidHeader): handler.deserialize(io.BytesIO(), 'multipart/form-data', 0) with pytest.raises(falcon.HTTPInvalidHeader): handler.deserialize(io.BytesIO(), 'multipart/form-data; boundary=', 0) overlong = '-' * 71 content_type = 'multipart/form-data; boundary=' + overlong with pytest.raises(falcon.HTTPInvalidHeader): handler.deserialize(io.BytesIO(), content_type, 0)
def test_body_part_properties(): handler = media.MultipartFormHandler() content_type = 'multipart/form-data; boundary=' + '5b11af82ab65407ba8cdccf37d2a9c4f' form = handler.deserialize(io.BytesIO(EXAMPLE1), content_type, len(EXAMPLE1)) for part in form: if part.content_type == 'application/json': assert part.name == part.name == 'document' elif part.name == 'file1': assert part.filename == part.filename == 'test.txt' assert part.secure_filename == part.filename
def _factory(options): multipart_handler = media.MultipartFormHandler() for key, value in options.items(): setattr(multipart_handler.parse_options, key, value) handlers = media.Handlers({ falcon.MEDIA_JSON: media.JSONHandler(), falcon.MEDIA_MULTIPART: multipart_handler, }) app = falcon.App() app.req_options.media_handlers = handlers app.add_route('/media', MultipartAnalyzer()) return testing.TestClient(app)
def test_empty_filename(): data = (b'--a0d738bcdb30449eb0d13f4b72c2897e\r\n' b'Content-Disposition: form-data; name="file"; filename=\r\n\r\n' b'An empty filename.\r\n' b'--a0d738bcdb30449eb0d13f4b72c2897e--\r\n') handler = media.MultipartFormHandler() content_type = 'multipart/form-data; boundary=' + 'a0d738bcdb30449eb0d13f4b72c2897e' stream = BufferedReader(io.BytesIO(data).read, len(data)) form = handler.deserialize(stream, content_type, len(data)) for part in form: assert part.filename == '' with pytest.raises(falcon.MediaMalformedError): part.secure_filename
def test_unknown_header(): data = (b'--BOUNDARY\r\n' b'Content-Disposition: form-data; name="empty"\r\n' b'Content-Coolness: fair\r\n' b'Content-Type: text/plain\r\n\r\n' b'\r\n' b'--BOUNDARY--\r\n') handler = media.MultipartFormHandler() form = handler.deserialize(io.BytesIO(data), 'multipart/form-data; boundary=BOUNDARY', len(data)) for part in form: assert part.data == b''
def test_parsing_correctness(buffer_size, chunk_size): example = EXAMPLES['boundary'] handler = media.MultipartFormHandler() stream = BufferedReader(io.BytesIO(example).read, len(example), buffer_size) form = handler.deserialize( stream, 'multipart/form-data; boundary=boundary', len(example)) for part in form: if part.name in ('lorem1', 'lorem2'): part_stream = part.stream result = [] while True: chunk = part_stream.read(chunk_size) if not chunk: break result.append(chunk) assert b''.join(result) == LOREM_IPSUM
def test_invalid_text_or_charset(charset, data): data = (b'--BOUNDARY\r\n' b'Content-Disposition: form-data; name="text"\r\n' b'Content-Type: text/plain; ' + 'charset={}\r\n\r\n'.format(charset).encode() + data + b'\r\n' b'--BOUNDARY\r\n' b'Content-Disposition: form-data; name="empty"\r\n' b'Content-Type: text/plain\r\n\r\n' b'\r\n' b'--BOUNDARY--\r\n') handler = media.MultipartFormHandler() form = handler.deserialize(io.BytesIO(data), 'multipart/form-data; boundary=BOUNDARY', len(data)) with pytest.raises(falcon.HTTPBadRequest): for part in form: part.text
def test_random_form(client): part_data = [os.urandom(random.randint(0, 2**18)) for _ in range(64)] form_data = b''.join( '--{}\r\n'.format(HASH_BOUNDARY).encode() + 'Content-Disposition: form-data; name="p{}"\r\n'.format(i).encode() + b'Content-Type: application/x-falcon-urandom\r\n\r\n' + part_data[i] + b'\r\n' for i in range(64) ) + '--{}--\r\n'.format(HASH_BOUNDARY).encode() handler = media.MultipartFormHandler() content_type = ('multipart/form-data; boundary=' + HASH_BOUNDARY) form = handler.deserialize( io.BytesIO(form_data), content_type, len(form_data)) for index, part in enumerate(form): assert part.content_type == 'application/x-falcon-urandom' assert part.data == part_data[index]
def test_unsupported_charset(): data = ( b'--BOUNDARY\r\n' b'Content-Disposition: form-data; name="text"\r\n' b'Content-Type: text/plain; charset=pecyn\r\n\r\n' b'AAHEHlRoZSBGYWxjb24gV2ViIEZyYW1ld29yaywgMjAxOQ==' b'\r\n' b'--BOUNDARY\r\n' b'Content-Disposition: form-data; name="empty"\r\n' b'Content-Type: text/plain\r\n\r\n' b'\r\n' b'--BOUNDARY--\r\n' ) handler = media.MultipartFormHandler() form = handler.deserialize( io.BytesIO(data), 'multipart/form-data; boundary=BOUNDARY', len(data)) with pytest.raises(falcon.HTTPBadRequest): for part in form: part.text
def test_content_transfer_encoding_header(): data = ( b'--BOUNDARY\r\n' b'Content-Disposition: form-data; name="file"; filename="bytes"\r\n' b'Content-Transfer-Encoding: Base64' b'Content-Type: application/x-falcon\r\n\r\n' b'UGVyZWdyaW5lIEZhbGNvbiADLgA=' b'\r\n' b'--BOUNDARY\r\n' b'Content-Disposition: form-data; name="empty"\r\n' b'Content-Type: text/plain\r\n\r\n' b'\r\n' b'--BOUNDARY--\r\n' ) handler = media.MultipartFormHandler() form = handler.deserialize( io.BytesIO(data), 'multipart/form-data; boundary=BOUNDARY', len(data)) with pytest.raises(falcon.HTTPBadRequest): for part in form: pass
def test_random_form(client): part_data = [os.urandom(random.randint(0, 2**18)) for _ in range(64)] form_data = b''.join( '--{}\r\n'.format(HASH_BOUNDARY).encode() + 'Content-Disposition: form-data; name="p{}"\r\n'.format(i).encode() + b'Content-Type: application/x-falcon-urandom\r\n\r\n' + part_data[i] + b'\r\n' for i in range(64)) + '--{}--\r\n'.format(HASH_BOUNDARY).encode() handler = media.MultipartFormHandler() content_type = ('multipart/form-data; boundary=' + HASH_BOUNDARY) form = handler.deserialize(io.BytesIO(form_data), content_type, len(form_data)) resp = client.simulate_post('/mirror', headers={'Content-Type': content_type}, body=form_data) assert resp.status_code == 200 form = msgpack.unpackb(resp.content, raw=False) for index, part in enumerate(form): assert part['content'] == part_data[index] assert part['content_type'] == 'application/x-falcon-urandom'
def test_headers_edge_cases(max_headers_size): data = ( b'--a0d738bcdb30449eb0d13f4b72c2897e\r\n' b'X-Falcon: Peregrine\r\n' b'Content-Type: application/vnd.oasis.opendocument.text\r\n' b'Junk\r\n' b'Content-Disposition: form-data; name="file"; filename=hd.txt\r\n\r\n' b'No, it is not an ODT document...\r\n' b'--a0d738bcdb30449eb0d13f4b72c2897e--\r\n' ) handler = media.MultipartFormHandler() handler.parse_options.max_body_part_headers_size = max_headers_size content_type = ('multipart/form-data; boundary=' + 'a0d738bcdb30449eb0d13f4b72c2897e') stream = BufferedReader(io.BytesIO(data).read, len(data)) form = handler.deserialize(stream, content_type, len(data)) if max_headers_size < 142: with pytest.raises(falcon.HTTPBadRequest): list(form) else: assert len(list(form)) == 1
def test_serialize(): handler = media.MultipartFormHandler() with pytest.raises(NotImplementedError): handler.serialize({'key': 'value'}, 'multipart/form-data')