예제 #1
0
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
예제 #2
0
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
예제 #3
0
    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)
예제 #4
0
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)
예제 #5
0
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',
    }
예제 #6
0
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)
예제 #7
0
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
예제 #8
0
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)
예제 #9
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
예제 #10
0
    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)
예제 #11
0
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
예제 #12
0
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''
예제 #13
0
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
예제 #14
0
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
예제 #15
0
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]
예제 #16
0
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
예제 #17
0
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
예제 #18
0
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'
예제 #19
0
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
예제 #20
0
def test_serialize():
    handler = media.MultipartFormHandler()
    with pytest.raises(NotImplementedError):
        handler.serialize({'key': 'value'}, 'multipart/form-data')