Support flow control for request streaming
This commit is contained in:
parent
e1c9020268
commit
fd279f8b6d
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Request Streaming
|
## Request Streaming
|
||||||
|
|
||||||
Sanic allows you to get request data by stream, as below. When the request ends, `request.stream.get()` returns `None`. Only post, put and patch decorator have stream argument.
|
Sanic allows you to get request data by stream, as below. When the request ends, `request.stream.get()` returns `None`. In order to do flow controll, calling `request.stream.task_done()` right after processing is required. Only post, put and patch decorator have stream argument.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
|
@ -26,6 +26,7 @@ class SimpleView(HTTPMethodView):
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
result += body.decode('utf-8')
|
result += body.decode('utf-8')
|
||||||
|
request.stream.task_done()
|
||||||
return text(result)
|
return text(result)
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ async def handler(request):
|
||||||
break
|
break
|
||||||
body = body.decode('utf-8').replace('1', 'A')
|
body = body.decode('utf-8').replace('1', 'A')
|
||||||
response.write(body)
|
response.write(body)
|
||||||
|
request.stream.task_done()
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,6 +51,7 @@ async def bp_handler(request):
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
result += body.decode('utf-8').replace('1', 'A')
|
result += body.decode('utf-8').replace('1', 'A')
|
||||||
|
request.stream.task_done()
|
||||||
return text(result)
|
return text(result)
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,6 +62,7 @@ async def post_handler(request):
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
result += body.decode('utf-8')
|
result += body.decode('utf-8')
|
||||||
|
request.stream.task_done()
|
||||||
return text(result)
|
return text(result)
|
||||||
|
|
||||||
app.blueprint(bp)
|
app.blueprint(bp)
|
||||||
|
|
|
@ -78,6 +78,7 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
def __init__(self, *, loop, request_handler, error_handler,
|
def __init__(self, *, loop, request_handler, error_handler,
|
||||||
signal=Signal(), connections=set(), request_timeout=60,
|
signal=Signal(), connections=set(), request_timeout=60,
|
||||||
response_timeout=60, keep_alive_timeout=5,
|
response_timeout=60, keep_alive_timeout=5,
|
||||||
|
request_max_queue_size=20,
|
||||||
request_max_size=None, request_class=None, access_log=True,
|
request_max_size=None, request_class=None, access_log=True,
|
||||||
keep_alive=True, is_request_stream=False, router=None,
|
keep_alive=True, is_request_stream=False, router=None,
|
||||||
state=None, debug=False, **kwargs):
|
state=None, debug=False, **kwargs):
|
||||||
|
@ -96,9 +97,11 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
self.request_timeout = request_timeout
|
self.request_timeout = request_timeout
|
||||||
self.response_timeout = response_timeout
|
self.response_timeout = response_timeout
|
||||||
self.keep_alive_timeout = keep_alive_timeout
|
self.keep_alive_timeout = keep_alive_timeout
|
||||||
|
self.request_max_queue_size = request_max_queue_size
|
||||||
self.request_max_size = request_max_size
|
self.request_max_size = request_max_size
|
||||||
self.request_class = request_class or Request
|
self.request_class = request_class or Request
|
||||||
self.is_request_stream = is_request_stream
|
self.is_request_stream = is_request_stream
|
||||||
|
self._paused = False
|
||||||
self._is_stream_handler = False
|
self._is_stream_handler = False
|
||||||
self._total_request_size = 0
|
self._total_request_size = 0
|
||||||
self._request_timeout_handler = None
|
self._request_timeout_handler = None
|
||||||
|
@ -230,6 +233,18 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
exception = InvalidUsage(message)
|
exception = InvalidUsage(message)
|
||||||
self.write_error(exception)
|
self.write_error(exception)
|
||||||
|
|
||||||
|
if self.is_request_stream and not self._paused and \
|
||||||
|
self.request is not None and self.request.stream:
|
||||||
|
if self.request.stream.qsize() > self.request_max_queue_size:
|
||||||
|
self.transport.pause_reading()
|
||||||
|
self._paused = True
|
||||||
|
self.loop.create_task(self.resume_reading())
|
||||||
|
|
||||||
|
async def resume_reading(self):
|
||||||
|
await self.request.stream.join()
|
||||||
|
self.transport.resume_reading()
|
||||||
|
self._paused = False
|
||||||
|
|
||||||
def on_url(self, url):
|
def on_url(self, url):
|
||||||
if not self.url:
|
if not self.url:
|
||||||
self.url = url
|
self.url = url
|
||||||
|
|
|
@ -29,6 +29,7 @@ def test_request_stream_method_view():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
result += body.decode('utf-8')
|
result += body.decode('utf-8')
|
||||||
|
request.stream.task_done()
|
||||||
return text(result)
|
return text(result)
|
||||||
|
|
||||||
app.add_route(SimpleView.as_view(), '/method_view')
|
app.add_route(SimpleView.as_view(), '/method_view')
|
||||||
|
@ -84,6 +85,7 @@ def test_request_stream_app():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
response.write(body.decode('utf-8'))
|
response.write(body.decode('utf-8'))
|
||||||
|
request.stream.task_done()
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
@app.put('/_put')
|
@app.put('/_put')
|
||||||
|
@ -101,6 +103,7 @@ def test_request_stream_app():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
response.write(body.decode('utf-8'))
|
response.write(body.decode('utf-8'))
|
||||||
|
request.stream.task_done()
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
@app.patch('/_patch')
|
@app.patch('/_patch')
|
||||||
|
@ -118,6 +121,7 @@ def test_request_stream_app():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
response.write(body.decode('utf-8'))
|
response.write(body.decode('utf-8'))
|
||||||
|
request.stream.task_done()
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
assert app.is_request_stream is True
|
assert app.is_request_stream is True
|
||||||
|
@ -178,6 +182,7 @@ def test_request_stream_handle_exception():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
response.write(body.decode('utf-8'))
|
response.write(body.decode('utf-8'))
|
||||||
|
request.stream.task_done()
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
# 404
|
# 404
|
||||||
|
@ -232,6 +237,7 @@ def test_request_stream_blueprint():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
response.write(body.decode('utf-8'))
|
response.write(body.decode('utf-8'))
|
||||||
|
request.stream.task_done()
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
@bp.put('/_put')
|
@bp.put('/_put')
|
||||||
|
@ -249,6 +255,7 @@ def test_request_stream_blueprint():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
response.write(body.decode('utf-8'))
|
response.write(body.decode('utf-8'))
|
||||||
|
request.stream.task_done()
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
@bp.patch('/_patch')
|
@bp.patch('/_patch')
|
||||||
|
@ -266,6 +273,7 @@ def test_request_stream_blueprint():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
response.write(body.decode('utf-8'))
|
response.write(body.decode('utf-8'))
|
||||||
|
request.stream.task_done()
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
app.blueprint(bp)
|
app.blueprint(bp)
|
||||||
|
@ -330,6 +338,7 @@ def test_request_stream_composition_view():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
result += body.decode('utf-8')
|
result += body.decode('utf-8')
|
||||||
|
request.stream.task_done()
|
||||||
return text(result)
|
return text(result)
|
||||||
|
|
||||||
view = CompositionView()
|
view = CompositionView()
|
||||||
|
@ -369,6 +378,7 @@ def test_request_stream():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
result += body.decode('utf-8')
|
result += body.decode('utf-8')
|
||||||
|
request.stream.task_done()
|
||||||
return text(result)
|
return text(result)
|
||||||
|
|
||||||
@app.post('/stream', stream=True)
|
@app.post('/stream', stream=True)
|
||||||
|
@ -381,6 +391,7 @@ def test_request_stream():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
response.write(body.decode('utf-8'))
|
response.write(body.decode('utf-8'))
|
||||||
|
request.stream.task_done()
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
@app.get('/get')
|
@app.get('/get')
|
||||||
|
@ -397,6 +408,7 @@ def test_request_stream():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
result += body.decode('utf-8')
|
result += body.decode('utf-8')
|
||||||
|
request.stream.task_done()
|
||||||
return text(result)
|
return text(result)
|
||||||
|
|
||||||
@bp.get('/bp_get')
|
@bp.get('/bp_get')
|
||||||
|
@ -416,6 +428,7 @@ def test_request_stream():
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
result += body.decode('utf-8')
|
result += body.decode('utf-8')
|
||||||
|
request.stream.task_done()
|
||||||
return text(result)
|
return text(result)
|
||||||
|
|
||||||
app.add_route(SimpleView.as_view(), '/method_view')
|
app.add_route(SimpleView.as_view(), '/method_view')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user