From 60b4efad67ba3b22dfaf83820c772cd7f5ff137c Mon Sep 17 00:00:00 2001 From: Kevin Guillaumond <46726602+KevinGDialpad@users.noreply.github.com> Date: Sat, 14 Mar 2020 08:57:39 -0700 Subject: [PATCH 1/2] Update config docs to match DEFAULT_CONFIG (#1803) * Set REAL_IP_HEADER's default value to "X-Real-IP" * Update config instead --- docs/sanic/config.rst | 14 ++++++++++++-- sanic/config.py | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/sanic/config.rst b/docs/sanic/config.rst index 0e34bcc7..366d22d1 100644 --- a/docs/sanic/config.rst +++ b/docs/sanic/config.rst @@ -115,15 +115,25 @@ Out of the box there are just a few predefined values which can be overwritten w +---------------------------+-------------------+-----------------------------------------------------------------------------+ | KEEP_ALIVE_TIMEOUT | 5 | How long to hold a TCP connection open (sec) | +---------------------------+-------------------+-----------------------------------------------------------------------------+ +| WEBSOCKET_MAX_SIZE | 2^20 | Maximum size for incoming messages (bytes) | ++---------------------------+-------------------+-----------------------------------------------------------------------------+ +| WEBSOCKET_MAX_QUEUE | 32 | Maximum length of the queue that holds incoming messages | ++---------------------------+-------------------+-----------------------------------------------------------------------------+ +| WEBSOCKET_READ_LIMIT | 2^16 | High-water limit of the buffer for incoming bytes | ++---------------------------+-------------------+-----------------------------------------------------------------------------+ +| WEBSOCKET_WRITE_LIMIT | 2^16 | High-water limit of the buffer for outgoing bytes | ++---------------------------+-------------------+-----------------------------------------------------------------------------+ | GRACEFUL_SHUTDOWN_TIMEOUT | 15.0 | How long to wait to force close non-idle connection (sec) | +---------------------------+-------------------+-----------------------------------------------------------------------------+ | ACCESS_LOG | True | Disable or enable access log | +---------------------------+-------------------+-----------------------------------------------------------------------------+ -| PROXIES_COUNT | -1 | The number of proxy servers in front of the app (e.g. nginx; see below) | +| FORWARDED_SECRET | None | Used to securely identify a specific proxy server (see below) | ++---------------------------+-------------------+-----------------------------------------------------------------------------+ +| PROXIES_COUNT | None | The number of proxy servers in front of the app (e.g. nginx; see below) | +---------------------------+-------------------+-----------------------------------------------------------------------------+ | FORWARDED_FOR_HEADER | "X-Forwarded-For" | The name of "X-Forwarded-For" HTTP header that contains client and proxy ip | +---------------------------+-------------------+-----------------------------------------------------------------------------+ -| REAL_IP_HEADER | "X-Real-IP" | The name of "X-Real-IP" HTTP header that contains real client ip | +| REAL_IP_HEADER | None | The name of "X-Real-IP" HTTP header that contains real client ip | +---------------------------+-------------------+-----------------------------------------------------------------------------+ The different Timeout variables: diff --git a/sanic/config.py b/sanic/config.py index 539ea45c..58de2110 100644 --- a/sanic/config.py +++ b/sanic/config.py @@ -20,7 +20,7 @@ DEFAULT_CONFIG = { "RESPONSE_TIMEOUT": 60, # 60 seconds "KEEP_ALIVE": True, "KEEP_ALIVE_TIMEOUT": 5, # 5 seconds - "WEBSOCKET_MAX_SIZE": 2 ** 20, # 1 megabytes + "WEBSOCKET_MAX_SIZE": 2 ** 20, # 1 megabyte "WEBSOCKET_MAX_QUEUE": 32, "WEBSOCKET_READ_LIMIT": 2 ** 16, "WEBSOCKET_WRITE_LIMIT": 2 ** 16, From 4db075ffc19a8dbf167bfd7614f4d4a6dad91c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=2E=20K=C3=A4rkk=C3=A4inen?= <98187+Tronic@users.noreply.github.com> Date: Tue, 24 Mar 2020 19:11:09 +0200 Subject: [PATCH 2/2] Streaming migration for 20.3 release (#1800) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Compatibility and deprecations for Sanic 20.3 in preparation of the streaming branch. * Add test for new API. * isort tests * More coverage * json takes str, not bytes Co-authored-by: L. Kärkkäinen --- sanic/request.py | 27 +++++++++++++++++++++++ tests/test_request_stream.py | 42 +++++++++++++++++++++++++++++++++++- tests/test_response.py | 2 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/sanic/request.py b/sanic/request.py index 9cab4017..dca15901 100644 --- a/sanic/request.py +++ b/sanic/request.py @@ -56,6 +56,14 @@ class StreamBuffer: self._queue.task_done() return payload + async def __aiter__(self): + """Support `async for data in request.stream`""" + while True: + data = await self.read() + if not data: + break + yield data + async def put(self, payload): await self._queue.put(payload) @@ -128,14 +136,33 @@ class Request: ) def body_init(self): + """.. deprecated:: 20.3""" self.body = [] def body_push(self, data): + """.. deprecated:: 20.3""" self.body.append(data) def body_finish(self): + """.. deprecated:: 20.3""" self.body = b"".join(self.body) + async def receive_body(self): + """Receive request.body, if not already received. + + Streaming handlers may call this to receive the full body. + + This is added as a compatibility shim in Sanic 20.3 because future + versions of Sanic will make all requests streaming and will use this + function instead of the non-async body_init/push/finish functions. + + Please make an issue if your code depends on the old functionality and + cannot be upgraded to the new API. + """ + if not self.stream: + return + self.body = b"".join([data async for data in self.stream]) + @property def json(self): if self.parsed_json is None: diff --git a/tests/test_request_stream.py b/tests/test_request_stream.py index b76e7248..17b0ac82 100644 --- a/tests/test_request_stream.py +++ b/tests/test_request_stream.py @@ -3,7 +3,7 @@ import pytest from sanic.blueprints import Blueprint from sanic.exceptions import HeaderExpectationFailed from sanic.request import StreamBuffer -from sanic.response import stream, text +from sanic.response import json, stream, text from sanic.views import CompositionView, HTTPMethodView from sanic.views import stream as stream_decorator @@ -613,3 +613,43 @@ def test_request_stream(app): request, response = app.test_client.post("/bp_stream", data=data) assert response.status == 200 assert response.text == data + +def test_streaming_new_api(app): + @app.post("/non-stream") + async def handler(request): + assert request.body == b"x" + await request.receive_body() # This should do nothing + assert request.body == b"x" + return text("OK") + + @app.post("/1", stream=True) + async def handler(request): + assert request.stream + assert not request.body + await request.receive_body() + return text(request.body.decode().upper()) + + @app.post("/2", stream=True) + async def handler(request): + ret = [] + async for data in request.stream: + # We should have no b"" or None, just proper chunks + assert data + assert isinstance(data, bytes) + ret.append(data.decode("ASCII")) + return json(ret) + + request, response = app.test_client.post("/non-stream", data="x") + assert response.status == 200 + + request, response = app.test_client.post("/1", data="TEST data") + assert request.body == b"TEST data" + assert response.status == 200 + assert response.text == "TEST DATA" + + request, response = app.test_client.post("/2", data=data) + assert response.status == 200 + res = response.json + assert isinstance(res, list) + assert len(res) > 1 + assert "".join(res) == data diff --git a/tests/test_response.py b/tests/test_response.py index aa72c3db..acb715db 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -15,6 +15,7 @@ from aiofiles import os as async_os from sanic.response import ( HTTPResponse, StreamingHTTPResponse, + empty, file, file_stream, json, @@ -22,7 +23,6 @@ from sanic.response import ( stream, text, ) -from sanic.response import empty from sanic.server import HttpProtocol from sanic.testing import HOST, PORT