New streaming test and minor fixes.
This commit is contained in:
parent
1aac4f546b
commit
01480c437b
|
@ -226,7 +226,9 @@ class Http:
|
|||
data = b""
|
||||
self.response_func = self.head_response_ignored
|
||||
headers["connection"] = "keep-alive" if self.keep_alive else "close"
|
||||
ret = format_http1_response(status, res.processed_headers) + data
|
||||
ret = format_http1_response(status, res.processed_headers)
|
||||
if data:
|
||||
ret += data
|
||||
# Send a 100-continue if expected and not Expectation Failed
|
||||
if self.expecting_continue:
|
||||
self.expecting_continue = False
|
||||
|
|
|
@ -54,6 +54,8 @@ class BaseHTTPResponse:
|
|||
"""
|
||||
if data is None and end_stream is None:
|
||||
end_stream = True
|
||||
if end_stream and not data and self.stream.send is None:
|
||||
return
|
||||
data = data.encode() if hasattr(data, "encode") else data or b""
|
||||
await self.stream.send(data, end_stream=end_stream)
|
||||
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import asyncio
|
||||
|
||||
from contextlib import closing
|
||||
from socket import socket
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.blueprints import Blueprint
|
||||
from sanic.exceptions import HeaderExpectationFailed
|
||||
from sanic.response import json, stream, text
|
||||
|
@ -582,5 +588,84 @@ def test_streaming_new_api(app):
|
|||
assert response.status == 200
|
||||
res = response.json
|
||||
assert isinstance(res, list)
|
||||
assert len(res) > 1
|
||||
assert "".join(res) == data
|
||||
|
||||
def test_streaming_echo():
|
||||
"""2-way streaming chat between server and client."""
|
||||
app = Sanic(name=__name__)
|
||||
|
||||
@app.post("/echo", stream=True)
|
||||
async def handler(request):
|
||||
res = await request.respond(content_type="text/plain; charset=utf-8")
|
||||
# Send headers
|
||||
await res.send(end_stream=False)
|
||||
# Echo back data (case swapped)
|
||||
async for data in request.stream:
|
||||
await res.send(data.swapcase())
|
||||
# Add EOF marker after successful operation
|
||||
await res.send(b"-", end_stream=True)
|
||||
|
||||
@app.listener("after_server_start")
|
||||
async def client_task(app, loop):
|
||||
try:
|
||||
reader, writer = await asyncio.open_connection(*addr)
|
||||
await client(app, reader, writer)
|
||||
finally:
|
||||
writer.close()
|
||||
app.stop()
|
||||
|
||||
async def client(app, reader, writer):
|
||||
# Unfortunately httpx does not support 2-way streaming, so do it by hand.
|
||||
host = f"host: {addr[0]}:{addr[1]}\r\n".encode()
|
||||
writer.write(
|
||||
b"POST /echo HTTP/1.1\r\n" + host + b"content-length: 2\r\n"
|
||||
b"content-type: text/plain; charset=utf-8\r\n"
|
||||
b"\r\n"
|
||||
)
|
||||
# Read response
|
||||
res = b""
|
||||
while not b"\r\n\r\n" in res:
|
||||
res += await reader.read(4096)
|
||||
assert res.startswith(b"HTTP/1.1 200 OK\r\n")
|
||||
assert res.endswith(b"\r\n\r\n")
|
||||
buffer = b""
|
||||
|
||||
async def read_chunk():
|
||||
nonlocal buffer
|
||||
while not b"\r\n" in buffer:
|
||||
data = await reader.read(4096)
|
||||
assert data
|
||||
buffer += data
|
||||
size, buffer = buffer.split(b"\r\n", 1)
|
||||
size = int(size, 16)
|
||||
if size == 0:
|
||||
return None
|
||||
while len(buffer) < size + 2:
|
||||
data = await reader.read(4096)
|
||||
assert data
|
||||
buffer += data
|
||||
print(res)
|
||||
assert buffer[size : size + 2] == b"\r\n"
|
||||
ret, buffer = buffer[:size], buffer[size + 2 :]
|
||||
return ret
|
||||
|
||||
# Chat with server
|
||||
writer.write(b"a")
|
||||
res = await read_chunk()
|
||||
assert res == b"A"
|
||||
|
||||
writer.write(b"b")
|
||||
res = await read_chunk()
|
||||
assert res == b"B"
|
||||
|
||||
res = await read_chunk()
|
||||
assert res == b"-"
|
||||
|
||||
res = await read_chunk()
|
||||
assert res == None
|
||||
|
||||
# Use random port for tests
|
||||
with closing(socket()) as sock:
|
||||
sock.bind(("127.0.0.1", 0))
|
||||
addr = sock.getsockname()
|
||||
app.run(sock=sock, access_log=False)
|
||||
|
|
Loading…
Reference in New Issue
Block a user