# -*- coding: utf-8 -*-
from distutils.core import setup

packages = \
['content_size_limit_asgi']

package_data = \
{'': ['*']}

install_requires = \
['starlette>=0.12.0,<0.13.0']

setup_kwargs = {
    'name': 'content-size-limit-asgi',
    'version': '0.1.4',
    'description': 'An ASGI3 middleware to implement maximum content size limits (mostly useful for HTTP uploads)',
    'long_description': '# content size limit\n\n[![CircleCI](https://circleci.com/gh/steinnes/content-size-limit-asgi.svg?style=svg)](https://circleci.com/gh/steinnes/content-size-limit-asgi)\n\nThis is a middleware for ASGI which intercepts the receive() method to raise\nan exception when the read bytes exceed the given limit.\n\n## Example\n\n```python\nimport uvicorn\n\nfrom starlette.applications import Starlette\nfrom starlette.responses import PlainTextResponse\n\nfrom content_size_limit_asgi import ContentSizeLimitMiddleware\n\n\napp = Starlette()\n\n@app.route("/", methods=["POST"])\nasync def index(request):\n    body = await request.body()\n    return PlainTextResponse(f"body: {body.decode(\'utf-8\')}")\n\n\napp.add_middleware(ContentSizeLimitMiddleware, max_content_size=512)\n\n\nif __name__ == "__main__":\n    uvicorn.run(app, host="127.0.0.1", port=6001, log_level=\'debug\')\n```\n\nTo test the app:\n\n```\n$ curl --limit-rate 5k -q -v http://localhost:6001/ -d `printf \'A%.0s\' {1..99999}`\n*   Trying 127.0.0.1...\n* TCP_NODELAY set\n* Connected to localhost (127.0.0.1) port 6001 (#0)\n> POST / HTTP/1.1\n> Host: localhost:6001\n> User-Agent: curl/7.54.0\n> Accept: */*\n> Content-Length: 99999\n> Content-Type: application/x-www-form-urlencoded\n> Expect: 100-continue\n>\n< HTTP/1.1 100 Continue\n< HTTP/1.1 500 Internal Server Error\n< date: Wed, 12 Jun 2019 14:41:28 GMT\n< server: uvicorn\n< content-length: 21\n< content-type: text/plain; charset=utf-8\n* HTTP error before end of send, stop sending\n<\n* Closing connection 0\nInternal Server Error\n```\n\nThe app console log should read:\n```\n$ PYTHONPATH=. python tapp.py\nINFO: Started server process [48160]\nINFO: Waiting for application startup.\nDEBUG: None - ASGI [1] Started\nWARNING 2019-06-12 14:42:18,003 content_size_limit.middleware: ASGI scope of type lifespan is not supported yet\nWARNING: ASGI scope of type lifespan is not supported yet\nDEBUG: None - ASGI [1] Sent {\'type\': \'lifespan.startup\'}\nDEBUG: None - ASGI [1] Received {\'type\': \'lifespan.startup.complete\'}\nINFO: Uvicorn running on http://127.0.0.1:6001 (Press CTRL+C to quit)\nDEBUG: (\'127.0.0.1\', 52103) - Connected\nDEBUG: (\'127.0.0.1\', 52103) - ASGI [2] Started\nDEBUG: (\'127.0.0.1\', 52103) - ASGI [2] Sent {\'type\': \'http.request\', \'body\': \'<16384 bytes>\', \'more_body\': True}\nDEBUG: (\'127.0.0.1\', 52103) - ASGI [2] Received {\'type\': \'http.response.start\', \'status\': 500, \'headers\': \'<...>\'}\nINFO: (\'127.0.0.1\', 52103) - "POST / HTTP/1.1" 500\nDEBUG: (\'127.0.0.1\', 52103) - ASGI [2] Received {\'type\': \'http.response.body\', \'body\': \'<21 bytes>\'}\nDEBUG: (\'127.0.0.1\', 52103) - ASGI [2] Raised exception\nERROR: Exception in ASGI application\nTraceback (most recent call last):\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/uvicorn/protocols/http/httptools_impl.py", line 368, in run_asgi\n    result = await app(self.scope, self.receive, self.send)\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/uvicorn/middleware/message_logger.py", line 58, in __call__\n    raise exc from None\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/uvicorn/middleware/message_logger.py", line 54, in __call__\n    await self.app(scope, inner_receive, inner_send)\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/starlette/applications.py", line 133, in __call__\n    await self.error_middleware(scope, receive, send)\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/starlette/middleware/errors.py", line 122, in __call__\n    raise exc from None\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/starlette/middleware/errors.py", line 100, in __call__\n    await self.app(scope, receive, _send)\n  File "/Users/ses/w/content-size-limit-asgi/content_size_limit/middleware.py", line 48, in __call__\n    await self.app(scope, wrapper, send)\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/starlette/exceptions.py", line 73, in __call__\n    raise exc from None\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/starlette/exceptions.py", line 62, in __call__\n    await self.app(scope, receive, sender)\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/starlette/routing.py", line 585, in __call__\n    await route(scope, receive, send)\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/starlette/routing.py", line 207, in __call__\n    await self.app(scope, receive, send)\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/starlette/routing.py", line 40, in app\n    response = await func(request)\n  File "tapp.py", line 13, in index\n    body = await request.body()\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/starlette/requests.py", line 167, in body\n    async for chunk in self.stream():\n  File "/Users/ses/.pyenv/versions/3.7.3/lib/python3.7/site-packages/starlette/requests.py", line 152, in stream\n    message = await self._receive()\n  File "/Users/ses/w/content-size-limit-asgi/content_size_limit/middleware.py", line 36, in inner\n    f"Maximum content size limit ({self.max_content_size}) exceeded ({received} bytes read)"\ncontent_size_limit.errors.ContentSizeExceeded: Maximum content size limit (512) exceeded (16384 bytes read)\nDEBUG: (\'127.0.0.1\', 52103) - Disconnected\n```\n\n## Why not just raise in the route / view functon itself?\n\nDepending on the ASGI server/framework used, you might not have access to\nthe raw stream to stop reading immediately once the maximum content size\nhas been exceeded.\n\nTake this Starlette view for example:\n\n\n```python\n\n@app.route("/documents/upload", methods=["POST"])\ndef upload_document(request):\n    data = await request.body()\n    if len(data) > Config.MAX_FILE_SIZE:\n        return api_400(\n            f"This file exceeds the maximum file size we support at this time ({Config.MAX_FILE_SIZE})",\n            code=MAX_FILE_SIZE_EXCEEDED,\n        )\n    ...\n```\n\nIf the maximum file size is 5MB, and the uploaded file was 50MB, then this\nimplementation reads the entire 50MB into memory before rejecting the\nrequest.\n',
    'author': 'Steinn Eldjárn Sigurðarson',
    'author_email': 'steinnes@gmail.com',
    'url': None,
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.6,<4.0',
}


setup(**setup_kwargs)
