Fix the handling of the end of a chunked request. (#2188)
* Fix the handling of the end of a chunked request. * Avoid hardcoding final chunk header size. * Add some unit tests for pipeline body reading * Decode bytes for json serialization Co-authored-by: L. Kärkkäinen <tronic@users.noreply.github.com> Co-authored-by: Adam Hopkins <adam@amhopkins.com>
This commit is contained in:
parent
1dd0332e8b
commit
08a4b3013f
|
@ -486,20 +486,24 @@ class Http:
|
|||
self.keep_alive = False
|
||||
raise InvalidUsage("Bad chunked encoding")
|
||||
|
||||
del buf[: pos + 2]
|
||||
|
||||
if size <= 0:
|
||||
self.request_body = None
|
||||
# Because we are leaving one CRLF in the buffer, we manually
|
||||
# reset the buffer here
|
||||
self.recv_buffer = bytearray()
|
||||
|
||||
if size < 0:
|
||||
self.keep_alive = False
|
||||
raise InvalidUsage("Bad chunked encoding")
|
||||
|
||||
# Consume CRLF, chunk size 0 and the two CRLF that follow
|
||||
pos += 4
|
||||
# Might need to wait for the final CRLF
|
||||
while len(buf) < pos:
|
||||
await self._receive_more()
|
||||
del buf[:pos]
|
||||
return None
|
||||
|
||||
# Remove CRLF, chunk size and the CRLF that follows
|
||||
del buf[: pos + 2]
|
||||
|
||||
self.request_bytes_left = size
|
||||
self.request_bytes += size
|
||||
|
||||
|
|
2
setup.py
2
setup.py
|
@ -93,7 +93,7 @@ requirements = [
|
|||
]
|
||||
|
||||
tests_require = [
|
||||
"sanic-testing>=0.6.0",
|
||||
"sanic-testing>=0.7.0b1",
|
||||
"pytest==5.2.1",
|
||||
"coverage==5.3",
|
||||
"gunicorn==20.0.4",
|
||||
|
|
82
tests/test_pipelining.py
Normal file
82
tests/test_pipelining.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
from httpx import AsyncByteStream
|
||||
from sanic_testing.reusable import ReusableClient
|
||||
|
||||
from sanic.response import json
|
||||
|
||||
|
||||
def test_no_body_requests(app):
|
||||
@app.get("/")
|
||||
async def handler(request):
|
||||
return json(
|
||||
{
|
||||
"request_id": str(request.id),
|
||||
"connection_id": id(request.conn_info),
|
||||
}
|
||||
)
|
||||
|
||||
client = ReusableClient(app, port=1234)
|
||||
|
||||
with client:
|
||||
_, response1 = client.get("/")
|
||||
_, response2 = client.get("/")
|
||||
|
||||
assert response1.status == response2.status == 200
|
||||
assert response1.json["request_id"] != response2.json["request_id"]
|
||||
assert response1.json["connection_id"] == response2.json["connection_id"]
|
||||
|
||||
|
||||
def test_json_body_requests(app):
|
||||
@app.post("/")
|
||||
async def handler(request):
|
||||
return json(
|
||||
{
|
||||
"request_id": str(request.id),
|
||||
"connection_id": id(request.conn_info),
|
||||
"foo": request.json.get("foo"),
|
||||
}
|
||||
)
|
||||
|
||||
client = ReusableClient(app, port=1234)
|
||||
|
||||
with client:
|
||||
_, response1 = client.post("/", json={"foo": True})
|
||||
_, response2 = client.post("/", json={"foo": True})
|
||||
|
||||
assert response1.status == response2.status == 200
|
||||
assert response1.json["foo"] is response2.json["foo"] is True
|
||||
assert response1.json["request_id"] != response2.json["request_id"]
|
||||
assert response1.json["connection_id"] == response2.json["connection_id"]
|
||||
|
||||
|
||||
def test_streaming_body_requests(app):
|
||||
@app.post("/", stream=True)
|
||||
async def handler(request):
|
||||
data = [part.decode("utf-8") async for part in request.stream]
|
||||
return json(
|
||||
{
|
||||
"request_id": str(request.id),
|
||||
"connection_id": id(request.conn_info),
|
||||
"data": data,
|
||||
}
|
||||
)
|
||||
|
||||
data = ["hello", "world"]
|
||||
|
||||
class Data(AsyncByteStream):
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
|
||||
async def __aiter__(self):
|
||||
for value in self.data:
|
||||
yield value.encode("utf-8")
|
||||
|
||||
client = ReusableClient(app, port=1234)
|
||||
|
||||
with client:
|
||||
_, response1 = client.post("/", data=Data(data))
|
||||
_, response2 = client.post("/", data=Data(data))
|
||||
|
||||
assert response1.status == response2.status == 200
|
||||
assert response1.json["data"] == response2.json["data"] == data
|
||||
assert response1.json["request_id"] != response2.json["request_id"]
|
||||
assert response1.json["connection_id"] == response2.json["connection_id"]
|
Loading…
Reference in New Issue
Block a user