Make all requests streaming and preload body for non-streaming handlers.

This commit is contained in:
L. Kärkkäinen 2020-02-21 13:28:50 +02:00
parent 6279eac3d1
commit fe64a2764d
11 changed files with 41 additions and 177 deletions

View File

@ -82,7 +82,6 @@ class Sanic:
self.strict_slashes = strict_slashes self.strict_slashes = strict_slashes
self.listeners = defaultdict(list) self.listeners = defaultdict(list)
self.is_running = False self.is_running = False
self.is_request_stream = False
self.websocket_enabled = False self.websocket_enabled = False
self.websocket_tasks = set() self.websocket_tasks = set()
self.named_request_middleware = {} self.named_request_middleware = {}
@ -187,9 +186,6 @@ class Sanic:
if not uri.startswith("/"): if not uri.startswith("/"):
uri = "/" + uri uri = "/" + uri
if stream:
self.is_request_stream = True
if strict_slashes is None: if strict_slashes is None:
strict_slashes = self.strict_slashes strict_slashes = self.strict_slashes
@ -956,6 +952,10 @@ class Sanic:
# Fetch handler from router # Fetch handler from router
handler, args, kwargs, uri, name = self.router.get(request) handler, args, kwargs, uri, name = self.router.get(request)
# Non-streaming handlers have their body preloaded
if not self.router.is_stream_handler(request):
await request.receive_body()
# -------------------------------------------- # # -------------------------------------------- #
# Request Middleware # Request Middleware
# -------------------------------------------- # # -------------------------------------------- #
@ -1381,7 +1381,7 @@ class Sanic:
server_settings = { server_settings = {
"protocol": protocol, "protocol": protocol,
"request_class": self.request_class, "request_class": self.request_class,
"is_request_stream": self.is_request_stream, "is_request_stream": True,
"router": self.router, "router": self.router,
"host": host, "host": host,
"port": port, "port": port,

View File

@ -190,7 +190,6 @@ class ASGIApp:
sanic_app: "sanic.app.Sanic" sanic_app: "sanic.app.Sanic"
request: Request request: Request
transport: MockTransport transport: MockTransport
do_stream: bool
lifespan: Lifespan lifespan: Lifespan
ws: Optional[WebSocketConnection] ws: Optional[WebSocketConnection]
@ -213,9 +212,6 @@ class ASGIApp:
for key, value in scope.get("headers", []) for key, value in scope.get("headers", [])
] ]
) )
instance.do_stream = (
True if headers.get("expect") == "100-continue" else False
)
instance.lifespan = Lifespan(instance) instance.lifespan = Lifespan(instance)
if scope["type"] == "lifespan": if scope["type"] == "lifespan":
@ -256,15 +252,9 @@ class ASGIApp:
sanic_app, sanic_app,
) )
if sanic_app.is_request_stream: instance.request.stream = StreamBuffer(
is_stream_handler = sanic_app.router.is_stream_handler( sanic_app.config.REQUEST_BUFFER_QUEUE_SIZE
instance.request )
)
if is_stream_handler:
instance.request.stream = StreamBuffer(
sanic_app.config.REQUEST_BUFFER_QUEUE_SIZE
)
instance.do_stream = True
return instance return instance
@ -300,10 +290,7 @@ class ASGIApp:
""" """
Handle the incoming request. Handle the incoming request.
""" """
if not self.do_stream: self.sanic_app.loop.create_task(self.stream_body())
self.request.body = await self.read_body()
else:
self.sanic_app.loop.create_task(self.stream_body())
handler = self.sanic_app.handle_request handler = self.sanic_app.handle_request
callback = None if self.ws else self.stream_callback callback = None if self.ws else self.stream_callback

View File

@ -116,7 +116,7 @@ class Request:
self.transport = transport self.transport = transport
# Init but do not inhale # Init but do not inhale
self.body_init() self.body = None
self.ctx = SimpleNamespace() self.ctx = SimpleNamespace()
self.parsed_forwarded = None self.parsed_forwarded = None
self.parsed_json = None self.parsed_json = None
@ -159,17 +159,7 @@ class Request:
Custom context is now stored in `request.custom_context.yourkey`""" Custom context is now stored in `request.custom_context.yourkey`"""
setattr(self.ctx, key, value) setattr(self.ctx, key, value)
def body_init(self):
self.body = []
def body_push(self, data):
self.body.append(data)
def body_finish(self):
self.body = b"".join(self.body)
async def receive_body(self): async def receive_body(self):
assert self.body == []
self.body = b"".join([data async for data in self.stream]) self.body = b"".join([data async for data in self.stream])
@property @property

View File

@ -328,14 +328,8 @@ class HttpProtocol(asyncio.Protocol):
self.expect_handler() self.expect_handler()
if self.is_request_stream: if self.is_request_stream:
self._is_stream_handler = self.router.is_stream_handler( self.request.stream = StreamBuffer(self.request_buffer_queue_size)
self.request self.execute_request_handler()
)
if self._is_stream_handler:
self.request.stream = StreamBuffer(
self.request_buffer_queue_size
)
self.execute_request_handler()
def expect_handler(self): def expect_handler(self):
""" """
@ -353,21 +347,18 @@ class HttpProtocol(asyncio.Protocol):
) )
def on_body(self, body): def on_body(self, body):
if self.is_request_stream and self._is_stream_handler: # body chunks can be put into asyncio.Queue out of order if
# body chunks can be put into asyncio.Queue out of order if # multiple tasks put concurrently and the queue is full in python
# multiple tasks put concurrently and the queue is full in python # 3.7. so we should not create more than one task putting into the
# 3.7. so we should not create more than one task putting into the # queue simultaneously.
# queue simultaneously. self._body_chunks.append(body)
self._body_chunks.append(body) if (
if ( not self._request_stream_task
not self._request_stream_task or self._request_stream_task.done()
or self._request_stream_task.done() ):
): self._request_stream_task = self.loop.create_task(
self._request_stream_task = self.loop.create_task( self.stream_append()
self.stream_append() )
)
else:
self.request.body_push(body)
async def body_append(self, body): async def body_append(self, body):
if ( if (
@ -385,7 +376,7 @@ class HttpProtocol(asyncio.Protocol):
await self.request.stream.put(body) await self.request.stream.put(body)
async def stream_append(self): async def stream_append(self):
while self._body_chunks: while self._body_chunks and self.request:
body = self._body_chunks.popleft() body = self._body_chunks.popleft()
if self.request.stream.is_full(): if self.request.stream.is_full():
self.transport.pause_reading() self.transport.pause_reading()
@ -393,6 +384,7 @@ class HttpProtocol(asyncio.Protocol):
self.transport.resume_reading() self.transport.resume_reading()
else: else:
await self.request.stream.put(body) await self.request.stream.put(body)
self._body_chunks.clear()
def on_message_complete(self): def on_message_complete(self):
# Entire request (headers and whole body) is received. # Entire request (headers and whole body) is received.
@ -400,18 +392,15 @@ class HttpProtocol(asyncio.Protocol):
if self._request_timeout_handler: if self._request_timeout_handler:
self._request_timeout_handler.cancel() self._request_timeout_handler.cancel()
self._request_timeout_handler = None self._request_timeout_handler = None
if self.is_request_stream and self._is_stream_handler:
self._body_chunks.append(None) self._body_chunks.append(None)
if ( if (
not self._request_stream_task not self._request_stream_task
or self._request_stream_task.done() or self._request_stream_task.done()
): ):
self._request_stream_task = self.loop.create_task( self._request_stream_task = self.loop.create_task(
self.stream_append() self.stream_append()
) )
return
self.request.body_finish()
self.execute_request_handler()
def execute_request_handler(self): def execute_request_handler(self):
""" """
@ -639,7 +628,6 @@ class HttpProtocol(asyncio.Protocol):
self._request_handler_task = None self._request_handler_task = None
self._request_stream_task = None self._request_stream_task = None
self._total_request_size = 0 self._total_request_size = 0
self._is_stream_handler = False
def close_if_idle(self): def close_if_idle(self):
"""Close the connection if a request is not being sent or received """Close the connection if a request is not being sent or received

View File

@ -71,7 +71,6 @@ def test_bp(app):
app.blueprint(bp) app.blueprint(bp)
request, response = app.test_client.get("/") request, response = app.test_client.get("/")
assert app.is_request_stream is False
assert response.text == "Hello" assert response.text == "Hello"
@ -383,48 +382,38 @@ def test_bp_shorthand(app):
@blueprint.get("/get") @blueprint.get("/get")
def handler(request): def handler(request):
assert request.stream is None
return text("OK") return text("OK")
@blueprint.put("/put") @blueprint.put("/put")
def put_handler(request): def put_handler(request):
assert request.stream is None
return text("OK") return text("OK")
@blueprint.post("/post") @blueprint.post("/post")
def post_handler(request): def post_handler(request):
assert request.stream is None
return text("OK") return text("OK")
@blueprint.head("/head") @blueprint.head("/head")
def head_handler(request): def head_handler(request):
assert request.stream is None
return text("OK") return text("OK")
@blueprint.options("/options") @blueprint.options("/options")
def options_handler(request): def options_handler(request):
assert request.stream is None
return text("OK") return text("OK")
@blueprint.patch("/patch") @blueprint.patch("/patch")
def patch_handler(request): def patch_handler(request):
assert request.stream is None
return text("OK") return text("OK")
@blueprint.delete("/delete") @blueprint.delete("/delete")
def delete_handler(request): def delete_handler(request):
assert request.stream is None
return text("OK") return text("OK")
@blueprint.websocket("/ws/", strict_slashes=True) @blueprint.websocket("/ws/", strict_slashes=True)
async def websocket_handler(request, ws): async def websocket_handler(request, ws):
assert request.stream is None
ev.set() ev.set()
app.blueprint(blueprint) app.blueprint(blueprint)
assert app.is_request_stream is False
request, response = app.test_client.get("/get") request, response = app.test_client.get("/get")
assert response.text == "OK" assert response.text == "OK"

View File

@ -37,8 +37,6 @@ def test_custom_request():
"/post", data=json_dumps(payload), headers=headers "/post", data=json_dumps(payload), headers=headers
) )
assert isinstance(request.body_buffer, BytesIO)
assert request.body_buffer.closed
assert request.body == b'{"test":"OK"}' assert request.body == b'{"test":"OK"}'
assert request.json.get("test") == "OK" assert request.json.get("test") == "OK"
assert response.text == "OK" assert response.text == "OK"
@ -46,8 +44,6 @@ def test_custom_request():
request, response = app.test_client.get("/get") request, response = app.test_client.get("/get")
assert isinstance(request.body_buffer, BytesIO)
assert request.body_buffer.closed
assert request.body == b"" assert request.body == b""
assert response.text == "OK" assert response.text == "OK"
assert response.status == 200 assert response.status == 200

View File

@ -85,5 +85,4 @@ def test_pickle_app_with_bp(app, protocol):
up_p_app = pickle.loads(p_app) up_p_app = pickle.loads(p_app)
assert up_p_app assert up_p_app
request, response = up_p_app.test_client.get("/") request, response = up_p_app.test_client.get("/")
assert up_p_app.is_request_stream is False
assert response.text == "Hello" assert response.text == "Hello"

View File

@ -107,10 +107,8 @@ def test_shorthand_named_routes_post(app):
def test_shorthand_named_routes_put(app): def test_shorthand_named_routes_put(app):
@app.put("/put", name="route_put") @app.put("/put", name="route_put")
def handler(request): def handler(request):
assert request.stream is None
return text("OK") return text("OK")
assert app.is_request_stream is False
assert app.router.routes_all["/put"].name == "route_put" assert app.router.routes_all["/put"].name == "route_put"
assert app.url_for("route_put") == "/put" assert app.url_for("route_put") == "/put"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
@ -120,10 +118,8 @@ def test_shorthand_named_routes_put(app):
def test_shorthand_named_routes_delete(app): def test_shorthand_named_routes_delete(app):
@app.delete("/delete", name="route_delete") @app.delete("/delete", name="route_delete")
def handler(request): def handler(request):
assert request.stream is None
return text("OK") return text("OK")
assert app.is_request_stream is False
assert app.router.routes_all["/delete"].name == "route_delete" assert app.router.routes_all["/delete"].name == "route_delete"
assert app.url_for("route_delete") == "/delete" assert app.url_for("route_delete") == "/delete"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
@ -133,10 +129,8 @@ def test_shorthand_named_routes_delete(app):
def test_shorthand_named_routes_patch(app): def test_shorthand_named_routes_patch(app):
@app.patch("/patch", name="route_patch") @app.patch("/patch", name="route_patch")
def handler(request): def handler(request):
assert request.stream is None
return text("OK") return text("OK")
assert app.is_request_stream is False
assert app.router.routes_all["/patch"].name == "route_patch" assert app.router.routes_all["/patch"].name == "route_patch"
assert app.url_for("route_patch") == "/patch" assert app.url_for("route_patch") == "/patch"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
@ -146,10 +140,8 @@ def test_shorthand_named_routes_patch(app):
def test_shorthand_named_routes_head(app): def test_shorthand_named_routes_head(app):
@app.head("/head", name="route_head") @app.head("/head", name="route_head")
def handler(request): def handler(request):
assert request.stream is None
return text("OK") return text("OK")
assert app.is_request_stream is False
assert app.router.routes_all["/head"].name == "route_head" assert app.router.routes_all["/head"].name == "route_head"
assert app.url_for("route_head") == "/head" assert app.url_for("route_head") == "/head"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):
@ -159,10 +151,8 @@ def test_shorthand_named_routes_head(app):
def test_shorthand_named_routes_options(app): def test_shorthand_named_routes_options(app):
@app.options("/options", name="route_options") @app.options("/options", name="route_options")
def handler(request): def handler(request):
assert request.stream is None
return text("OK") return text("OK")
assert app.is_request_stream is False
assert app.router.routes_all["/options"].name == "route_options" assert app.router.routes_all["/options"].name == "route_options"
assert app.url_for("route_options") == "/options" assert app.url_for("route_options") == "/options"
with pytest.raises(URLBuildError): with pytest.raises(URLBuildError):

View File

@ -12,28 +12,23 @@ data = "abc" * 10000000
def test_request_stream_method_view(app): def test_request_stream_method_view(app):
"""for self.is_request_stream = True"""
class SimpleView(HTTPMethodView): class SimpleView(HTTPMethodView):
def get(self, request): def get(self, request):
assert request.stream is None
return text("OK") return text("OK")
@stream_decorator @stream_decorator
async def post(self, request): async def post(self, request):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
result = "" result = b""
while True: while True:
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
result += body.decode("utf-8") result += body
return text(result) return text(result.decode())
app.add_route(SimpleView.as_view(), "/method_view") app.add_route(SimpleView.as_view(), "/method_view")
assert app.is_request_stream is True
request, response = app.test_client.get("/method_view") request, response = app.test_client.get("/method_view")
assert response.status == 200 assert response.status == 200
assert response.text == "OK" assert response.text == "OK"
@ -65,8 +60,6 @@ def test_request_stream_100_continue(app, headers, expect_raise_exception):
app.add_route(SimpleView.as_view(), "/method_view") app.add_route(SimpleView.as_view(), "/method_view")
assert app.is_request_stream is True
if not expect_raise_exception: if not expect_raise_exception:
request, response = app.test_client.post( request, response = app.test_client.post(
"/method_view", data=data, headers={"EXPECT": "100-continue"} "/method_view", data=data, headers={"EXPECT": "100-continue"}
@ -84,31 +77,24 @@ def test_request_stream_100_continue(app, headers, expect_raise_exception):
def test_request_stream_app(app): def test_request_stream_app(app):
"""for self.is_request_stream = True and decorators"""
@app.get("/get") @app.get("/get")
async def get(request): async def get(request):
assert request.stream is None
return text("GET") return text("GET")
@app.head("/head") @app.head("/head")
async def head(request): async def head(request):
assert request.stream is None
return text("HEAD") return text("HEAD")
@app.delete("/delete") @app.delete("/delete")
async def delete(request): async def delete(request):
assert request.stream is None
return text("DELETE") return text("DELETE")
@app.options("/options") @app.options("/options")
async def options(request): async def options(request):
assert request.stream is None
return text("OPTIONS") return text("OPTIONS")
@app.post("/_post/<id>") @app.post("/_post/<id>")
async def _post(request, id): async def _post(request, id):
assert request.stream is None
return text("_POST") return text("_POST")
@app.post("/post/<id>", stream=True) @app.post("/post/<id>", stream=True)
@ -124,7 +110,6 @@ def test_request_stream_app(app):
@app.put("/_put") @app.put("/_put")
async def _put(request): async def _put(request):
assert request.stream is None
return text("_PUT") return text("_PUT")
@app.put("/put", stream=True) @app.put("/put", stream=True)
@ -140,7 +125,6 @@ def test_request_stream_app(app):
@app.patch("/_patch") @app.patch("/_patch")
async def _patch(request): async def _patch(request):
assert request.stream is None
return text("_PATCH") return text("_PATCH")
@app.patch("/patch", stream=True) @app.patch("/patch", stream=True)
@ -154,8 +138,6 @@ def test_request_stream_app(app):
result += body.decode("utf-8") result += body.decode("utf-8")
return text(result) return text(result)
assert app.is_request_stream is True
request, response = app.test_client.get("/get") request, response = app.test_client.get("/get")
assert response.status == 200 assert response.status == 200
assert response.text == "GET" assert response.text == "GET"
@ -199,31 +181,24 @@ def test_request_stream_app(app):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_request_stream_app_asgi(app): async def test_request_stream_app_asgi(app):
"""for self.is_request_stream = True and decorators"""
@app.get("/get") @app.get("/get")
async def get(request): async def get(request):
assert request.stream is None
return text("GET") return text("GET")
@app.head("/head") @app.head("/head")
async def head(request): async def head(request):
assert request.stream is None
return text("HEAD") return text("HEAD")
@app.delete("/delete") @app.delete("/delete")
async def delete(request): async def delete(request):
assert request.stream is None
return text("DELETE") return text("DELETE")
@app.options("/options") @app.options("/options")
async def options(request): async def options(request):
assert request.stream is None
return text("OPTIONS") return text("OPTIONS")
@app.post("/_post/<id>") @app.post("/_post/<id>")
async def _post(request, id): async def _post(request, id):
assert request.stream is None
return text("_POST") return text("_POST")
@app.post("/post/<id>", stream=True) @app.post("/post/<id>", stream=True)
@ -239,7 +214,6 @@ async def test_request_stream_app_asgi(app):
@app.put("/_put") @app.put("/_put")
async def _put(request): async def _put(request):
assert request.stream is None
return text("_PUT") return text("_PUT")
@app.put("/put", stream=True) @app.put("/put", stream=True)
@ -255,7 +229,6 @@ async def test_request_stream_app_asgi(app):
@app.patch("/_patch") @app.patch("/_patch")
async def _patch(request): async def _patch(request):
assert request.stream is None
return text("_PATCH") return text("_PATCH")
@app.patch("/patch", stream=True) @app.patch("/patch", stream=True)
@ -269,8 +242,6 @@ async def test_request_stream_app_asgi(app):
result += body.decode("utf-8") result += body.decode("utf-8")
return text(result) return text(result)
assert app.is_request_stream is True
request, response = await app.asgi_client.get("/get") request, response = await app.asgi_client.get("/get")
assert response.status == 200 assert response.status == 200
assert response.text == "GET" assert response.text == "GET"
@ -318,13 +289,13 @@ def test_request_stream_handle_exception(app):
@app.post("/post/<id>", stream=True) @app.post("/post/<id>", stream=True)
async def post(request, id): async def post(request, id):
assert isinstance(request.stream, StreamBuffer) assert isinstance(request.stream, StreamBuffer)
result = "" result = b""
while True: while True:
body = await request.stream.read() body = await request.stream.read()
if body is None: if body is None:
break break
result += body.decode("utf-8") result += body
return text(result) return text(result.decode())
# 404 # 404
request, response = app.test_client.post("/in_valid_post", data=data) request, response = app.test_client.post("/in_valid_post", data=data)
@ -338,32 +309,26 @@ def test_request_stream_handle_exception(app):
def test_request_stream_blueprint(app): def test_request_stream_blueprint(app):
"""for self.is_request_stream = True"""
bp = Blueprint("test_blueprint_request_stream_blueprint") bp = Blueprint("test_blueprint_request_stream_blueprint")
@app.get("/get") @app.get("/get")
async def get(request): async def get(request):
assert request.stream is None
return text("GET") return text("GET")
@bp.head("/head") @bp.head("/head")
async def head(request): async def head(request):
assert request.stream is None
return text("HEAD") return text("HEAD")
@bp.delete("/delete") @bp.delete("/delete")
async def delete(request): async def delete(request):
assert request.stream is None
return text("DELETE") return text("DELETE")
@bp.options("/options") @bp.options("/options")
async def options(request): async def options(request):
assert request.stream is None
return text("OPTIONS") return text("OPTIONS")
@bp.post("/_post/<id>") @bp.post("/_post/<id>")
async def _post(request, id): async def _post(request, id):
assert request.stream is None
return text("_POST") return text("_POST")
@bp.post("/post/<id>", stream=True) @bp.post("/post/<id>", stream=True)
@ -379,7 +344,6 @@ def test_request_stream_blueprint(app):
@bp.put("/_put") @bp.put("/_put")
async def _put(request): async def _put(request):
assert request.stream is None
return text("_PUT") return text("_PUT")
@bp.put("/put", stream=True) @bp.put("/put", stream=True)
@ -395,7 +359,6 @@ def test_request_stream_blueprint(app):
@bp.patch("/_patch") @bp.patch("/_patch")
async def _patch(request): async def _patch(request):
assert request.stream is None
return text("_PATCH") return text("_PATCH")
@bp.patch("/patch", stream=True) @bp.patch("/patch", stream=True)
@ -424,8 +387,6 @@ def test_request_stream_blueprint(app):
) )
app.blueprint(bp) app.blueprint(bp)
assert app.is_request_stream is True
request, response = app.test_client.get("/get") request, response = app.test_client.get("/get")
assert response.status == 200 assert response.status == 200
assert response.text == "GET" assert response.text == "GET"
@ -472,10 +433,7 @@ def test_request_stream_blueprint(app):
def test_request_stream_composition_view(app): def test_request_stream_composition_view(app):
"""for self.is_request_stream = True"""
def get_handler(request): def get_handler(request):
assert request.stream is None
return text("OK") return text("OK")
async def post_handler(request): async def post_handler(request):
@ -493,8 +451,6 @@ def test_request_stream_composition_view(app):
view.add(["POST"], post_handler, stream=True) view.add(["POST"], post_handler, stream=True)
app.add_route(view, "/composition_view") app.add_route(view, "/composition_view")
assert app.is_request_stream is True
request, response = app.test_client.get("/composition_view") request, response = app.test_client.get("/composition_view")
assert response.status == 200 assert response.status == 200
assert response.text == "OK" assert response.text == "OK"
@ -510,7 +466,6 @@ def test_request_stream(app):
class SimpleView(HTTPMethodView): class SimpleView(HTTPMethodView):
def get(self, request): def get(self, request):
assert request.stream is None
return text("OK") return text("OK")
@stream_decorator @stream_decorator
@ -537,7 +492,6 @@ def test_request_stream(app):
@app.get("/get") @app.get("/get")
async def get(request): async def get(request):
assert request.stream is None
return text("OK") return text("OK")
@bp.post("/bp_stream", stream=True) @bp.post("/bp_stream", stream=True)
@ -553,11 +507,9 @@ def test_request_stream(app):
@bp.get("/bp_get") @bp.get("/bp_get")
async def bp_get(request): async def bp_get(request):
assert request.stream is None
return text("OK") return text("OK")
def get_handler(request): def get_handler(request):
assert request.stream is None
return text("OK") return text("OK")
async def post_handler(request): async def post_handler(request):
@ -580,8 +532,6 @@ def test_request_stream(app):
app.add_route(view, "/composition_view") app.add_route(view, "/composition_view")
assert app.is_request_stream is True
request, response = app.test_client.get("/method_view") request, response = app.test_client.get("/method_view")
assert response.status == 200 assert response.status == 200
assert response.text == "OK" assert response.text == "OK"

View File

@ -66,16 +66,12 @@ def test_shorthand_routes_multiple(app):
def test_route_strict_slash(app): def test_route_strict_slash(app):
@app.get("/get", strict_slashes=True) @app.get("/get", strict_slashes=True)
def handler1(request): def handler1(request):
assert request.stream is None
return text("OK") return text("OK")
@app.post("/post/", strict_slashes=True) @app.post("/post/", strict_slashes=True)
def handler2(request): def handler2(request):
assert request.stream is None
return text("OK") return text("OK")
assert app.is_request_stream is False
request, response = app.test_client.get("/get") request, response = app.test_client.get("/get")
assert response.text == "OK" assert response.text == "OK"
@ -214,11 +210,8 @@ def test_shorthand_routes_post(app):
def test_shorthand_routes_put(app): def test_shorthand_routes_put(app):
@app.put("/put") @app.put("/put")
def handler(request): def handler(request):
assert request.stream is None
return text("OK") return text("OK")
assert app.is_request_stream is False
request, response = app.test_client.put("/put") request, response = app.test_client.put("/put")
assert response.text == "OK" assert response.text == "OK"
@ -229,11 +222,8 @@ def test_shorthand_routes_put(app):
def test_shorthand_routes_delete(app): def test_shorthand_routes_delete(app):
@app.delete("/delete") @app.delete("/delete")
def handler(request): def handler(request):
assert request.stream is None
return text("OK") return text("OK")
assert app.is_request_stream is False
request, response = app.test_client.delete("/delete") request, response = app.test_client.delete("/delete")
assert response.text == "OK" assert response.text == "OK"
@ -244,11 +234,8 @@ def test_shorthand_routes_delete(app):
def test_shorthand_routes_patch(app): def test_shorthand_routes_patch(app):
@app.patch("/patch") @app.patch("/patch")
def handler(request): def handler(request):
assert request.stream is None
return text("OK") return text("OK")
assert app.is_request_stream is False
request, response = app.test_client.patch("/patch") request, response = app.test_client.patch("/patch")
assert response.text == "OK" assert response.text == "OK"
@ -259,11 +246,8 @@ def test_shorthand_routes_patch(app):
def test_shorthand_routes_head(app): def test_shorthand_routes_head(app):
@app.head("/head") @app.head("/head")
def handler(request): def handler(request):
assert request.stream is None
return text("OK") return text("OK")
assert app.is_request_stream is False
request, response = app.test_client.head("/head") request, response = app.test_client.head("/head")
assert response.status == 200 assert response.status == 200
@ -274,11 +258,8 @@ def test_shorthand_routes_head(app):
def test_shorthand_routes_options(app): def test_shorthand_routes_options(app):
@app.options("/options") @app.options("/options")
def handler(request): def handler(request):
assert request.stream is None
return text("OK") return text("OK")
assert app.is_request_stream is False
request, response = app.test_client.options("/options") request, response = app.test_client.options("/options")
assert response.status == 200 assert response.status == 200

View File

@ -12,7 +12,6 @@ from sanic.views import CompositionView, HTTPMethodView
def test_methods(app, method): def test_methods(app, method):
class DummyView(HTTPMethodView): class DummyView(HTTPMethodView):
async def get(self, request): async def get(self, request):
assert request.stream is None
return text("", headers={"method": "GET"}) return text("", headers={"method": "GET"})
def post(self, request): def post(self, request):
@ -34,7 +33,6 @@ def test_methods(app, method):
return text("", headers={"method": "DELETE"}) return text("", headers={"method": "DELETE"})
app.add_route(DummyView.as_view(), "/") app.add_route(DummyView.as_view(), "/")
assert app.is_request_stream is False
request, response = getattr(app.test_client, method.lower())("/") request, response = getattr(app.test_client, method.lower())("/")
assert response.headers["method"] == method assert response.headers["method"] == method
@ -69,7 +67,6 @@ def test_with_bp(app):
class DummyView(HTTPMethodView): class DummyView(HTTPMethodView):
def get(self, request): def get(self, request):
assert request.stream is None
return text("I am get method") return text("I am get method")
bp.add_route(DummyView.as_view(), "/") bp.add_route(DummyView.as_view(), "/")
@ -77,7 +74,6 @@ def test_with_bp(app):
app.blueprint(bp) app.blueprint(bp)
request, response = app.test_client.get("/") request, response = app.test_client.get("/")
assert app.is_request_stream is False
assert response.text == "I am get method" assert response.text == "I am get method"
@ -211,14 +207,12 @@ def test_composition_view_runs_methods_as_expected(app, method):
view = CompositionView() view = CompositionView()
def first(request): def first(request):
assert request.stream is None
return text("first method") return text("first method")
view.add(["GET", "POST", "PUT"], first) view.add(["GET", "POST", "PUT"], first)
view.add(["DELETE", "PATCH"], lambda x: text("second method")) view.add(["DELETE", "PATCH"], lambda x: text("second method"))
app.add_route(view, "/") app.add_route(view, "/")
assert app.is_request_stream is False
if method in ["GET", "POST", "PUT"]: if method in ["GET", "POST", "PUT"]:
request, response = getattr(app.test_client, method.lower())("/") request, response = getattr(app.test_client, method.lower())("/")