squash
This commit is contained in:
parent
c09129ec63
commit
eb3d0a3f87
|
@ -291,7 +291,6 @@ class ASGIApp:
|
|||
"""
|
||||
Write the response.
|
||||
"""
|
||||
response.asgi = True
|
||||
headers: List[Tuple[bytes, bytes]] = []
|
||||
cookies: Dict[str, str] = {}
|
||||
try:
|
||||
|
@ -324,6 +323,8 @@ class ASGIApp:
|
|||
if name not in (b"Set-Cookie",)
|
||||
]
|
||||
|
||||
response.asgi = True
|
||||
|
||||
if "content-length" not in response.headers and not isinstance(
|
||||
response, StreamingHTTPResponse
|
||||
):
|
||||
|
|
|
@ -129,27 +129,27 @@ class Request:
|
|||
|
||||
def get(self, key, default=None):
|
||||
""".. deprecated:: 19.9
|
||||
Custom context is now stored in `request.custom_context.yourkey`"""
|
||||
Custom context is now stored in `request.custom_context.yourkey`"""
|
||||
return self.ctx.__dict__.get(key, default)
|
||||
|
||||
def __contains__(self, key):
|
||||
""".. deprecated:: 19.9
|
||||
Custom context is now stored in `request.custom_context.yourkey`"""
|
||||
Custom context is now stored in `request.custom_context.yourkey`"""
|
||||
return key in self.ctx.__dict__
|
||||
|
||||
def __getitem__(self, key):
|
||||
""".. deprecated:: 19.9
|
||||
Custom context is now stored in `request.custom_context.yourkey`"""
|
||||
Custom context is now stored in `request.custom_context.yourkey`"""
|
||||
return self.ctx.__dict__[key]
|
||||
|
||||
def __delitem__(self, key):
|
||||
""".. deprecated:: 19.9
|
||||
Custom context is now stored in `request.custom_context.yourkey`"""
|
||||
Custom context is now stored in `request.custom_context.yourkey`"""
|
||||
del self.ctx.__dict__[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
""".. deprecated:: 19.9
|
||||
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)
|
||||
|
||||
def body_init(self):
|
||||
|
|
|
@ -10,6 +10,7 @@ from sanic.cookies import CookieJar
|
|||
from sanic.headers import format_http1
|
||||
from sanic.helpers import STATUS_CODES, has_message_body, remove_entity_headers
|
||||
|
||||
|
||||
try:
|
||||
from ujson import dumps as json_dumps
|
||||
except ImportError:
|
||||
|
@ -80,14 +81,18 @@ class StreamingHTTPResponse(BaseHTTPResponse):
|
|||
await self.protocol.push_data(data)
|
||||
await self.protocol.drain()
|
||||
|
||||
async def stream(self, version="1.1", keep_alive=False, keep_alive_timeout=None):
|
||||
async def stream(
|
||||
self, version="1.1", keep_alive=False, keep_alive_timeout=None
|
||||
):
|
||||
"""Streams headers, runs the `streaming_fn` callback that writes
|
||||
content to the response body, then finalizes the response body.
|
||||
"""
|
||||
if version != "1.1":
|
||||
self.chunked = False
|
||||
headers = self.get_headers(
|
||||
version, keep_alive=keep_alive, keep_alive_timeout=keep_alive_timeout,
|
||||
version,
|
||||
keep_alive=keep_alive,
|
||||
keep_alive_timeout=keep_alive_timeout,
|
||||
)
|
||||
if not getattr(self, "asgi", False):
|
||||
await self.protocol.push_data(headers)
|
||||
|
@ -98,7 +103,9 @@ class StreamingHTTPResponse(BaseHTTPResponse):
|
|||
# no need to await drain here after this write, because it is the
|
||||
# very last thing we write and nothing needs to wait for it.
|
||||
|
||||
def get_headers(self, version="1.1", keep_alive=False, keep_alive_timeout=None):
|
||||
def get_headers(
|
||||
self, version="1.1", keep_alive=False, keep_alive_timeout=None
|
||||
):
|
||||
# This is all returned in a kind-of funky way
|
||||
# We tried to make this as fast as possible in pure python
|
||||
timeout_header = b""
|
||||
|
@ -132,7 +139,12 @@ class HTTPResponse(BaseHTTPResponse):
|
|||
__slots__ = ("body", "status", "content_type", "headers", "_cookies")
|
||||
|
||||
def __init__(
|
||||
self, body=None, status=200, headers=None, content_type=None, body_bytes=b"",
|
||||
self,
|
||||
body=None,
|
||||
status=200,
|
||||
headers=None,
|
||||
content_type=None,
|
||||
body_bytes=b"",
|
||||
):
|
||||
self.content_type = content_type
|
||||
|
||||
|
@ -173,7 +185,9 @@ class HTTPResponse(BaseHTTPResponse):
|
|||
else:
|
||||
status = STATUS_CODES.get(self.status, b"UNKNOWN RESPONSE")
|
||||
|
||||
return (b"HTTP/%b %d %b\r\n" b"Connection: %b\r\n" b"%b" b"%b\r\n" b"%b") % (
|
||||
return (
|
||||
b"HTTP/%b %d %b\r\n" b"Connection: %b\r\n" b"%b" b"%b\r\n" b"%b"
|
||||
) % (
|
||||
version.encode(),
|
||||
self.status,
|
||||
status,
|
||||
|
@ -224,7 +238,9 @@ def json(
|
|||
)
|
||||
|
||||
|
||||
def text(body, status=200, headers=None, content_type="text/plain; charset=utf-8"):
|
||||
def text(
|
||||
body, status=200, headers=None, content_type="text/plain; charset=utf-8"
|
||||
):
|
||||
"""
|
||||
Returns response object with body in text format.
|
||||
|
||||
|
@ -233,10 +249,14 @@ def text(body, status=200, headers=None, content_type="text/plain; charset=utf-8
|
|||
:param headers: Custom Headers.
|
||||
:param content_type: the content type (string) of the response
|
||||
"""
|
||||
return HTTPResponse(body, status=status, headers=headers, content_type=content_type)
|
||||
return HTTPResponse(
|
||||
body, status=status, headers=headers, content_type=content_type
|
||||
)
|
||||
|
||||
|
||||
def raw(body, status=200, headers=None, content_type="application/octet-stream"):
|
||||
def raw(
|
||||
body, status=200, headers=None, content_type="application/octet-stream"
|
||||
):
|
||||
"""
|
||||
Returns response object without encoding the body.
|
||||
|
||||
|
@ -246,7 +266,10 @@ def raw(body, status=200, headers=None, content_type="application/octet-stream")
|
|||
:param content_type: the content type (string) of the response.
|
||||
"""
|
||||
return HTTPResponse(
|
||||
body_bytes=body, status=status, headers=headers, content_type=content_type,
|
||||
body_bytes=body,
|
||||
status=status,
|
||||
headers=headers,
|
||||
content_type=content_type,
|
||||
)
|
||||
|
||||
|
||||
|
@ -259,12 +282,20 @@ def html(body, status=200, headers=None):
|
|||
:param headers: Custom Headers.
|
||||
"""
|
||||
return HTTPResponse(
|
||||
body, status=status, headers=headers, content_type="text/html; charset=utf-8",
|
||||
body,
|
||||
status=status,
|
||||
headers=headers,
|
||||
content_type="text/html; charset=utf-8",
|
||||
)
|
||||
|
||||
|
||||
async def file(
|
||||
location, status=200, mime_type=None, headers=None, filename=None, _range=None,
|
||||
location,
|
||||
status=200,
|
||||
mime_type=None,
|
||||
headers=None,
|
||||
filename=None,
|
||||
_range=None,
|
||||
):
|
||||
"""Return a response object with file data.
|
||||
|
||||
|
@ -296,7 +327,10 @@ async def file(
|
|||
|
||||
mime_type = mime_type or guess_type(filename)[0] or "text/plain"
|
||||
return HTTPResponse(
|
||||
status=status, headers=headers, content_type=mime_type, body_bytes=out_stream,
|
||||
status=status,
|
||||
headers=headers,
|
||||
content_type=mime_type,
|
||||
body_bytes=out_stream,
|
||||
)
|
||||
|
||||
|
||||
|
@ -404,7 +438,9 @@ def stream(
|
|||
)
|
||||
|
||||
|
||||
def redirect(to, headers=None, status=302, content_type="text/html; charset=utf-8"):
|
||||
def redirect(
|
||||
to, headers=None, status=302, content_type="text/html; charset=utf-8"
|
||||
):
|
||||
"""Abort execution and cause a 302 redirect (by default).
|
||||
|
||||
:param to: path or fully qualified URL to redirect to
|
||||
|
@ -421,5 +457,6 @@ def redirect(to, headers=None, status=302, content_type="text/html; charset=utf-
|
|||
# According to RFC 7231, a relative URI is now permitted.
|
||||
headers["Location"] = safe_to
|
||||
|
||||
return HTTPResponse(status=status, headers=headers, content_type=content_type)
|
||||
|
||||
return HTTPResponse(
|
||||
status=status, headers=headers, content_type=content_type
|
||||
)
|
||||
|
|
|
@ -484,7 +484,7 @@ class Router:
|
|||
return route_handler, [], kwargs, route.uri, route.name
|
||||
|
||||
def is_stream_handler(self, request):
|
||||
""" Handler for request is stream or not.
|
||||
"""Handler for request is stream or not.
|
||||
:param request: Request object
|
||||
:return: bool
|
||||
"""
|
||||
|
|
|
@ -174,7 +174,7 @@ class GunicornWorker(base.Worker):
|
|||
|
||||
@staticmethod
|
||||
def _create_ssl_context(cfg):
|
||||
""" Creates SSLContext instance for usage in asyncio.create_server.
|
||||
"""Creates SSLContext instance for usage in asyncio.create_server.
|
||||
See ssl.SSLSocket.__init__ for more details.
|
||||
"""
|
||||
ctx = ssl.SSLContext(cfg.ssl_version)
|
||||
|
|
|
@ -230,8 +230,8 @@ async def handler3(request):
|
|||
|
||||
def test_keep_alive_timeout_reuse():
|
||||
"""If the server keep-alive timeout and client keep-alive timeout are
|
||||
both longer than the delay, the client _and_ server will successfully
|
||||
reuse the existing connection."""
|
||||
both longer than the delay, the client _and_ server will successfully
|
||||
reuse the existing connection."""
|
||||
try:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import asyncio
|
||||
import inspect
|
||||
import os
|
||||
|
||||
from collections import namedtuple
|
||||
from mimetypes import guess_type
|
||||
from random import choice
|
||||
|
@ -8,6 +9,7 @@ from unittest.mock import MagicMock
|
|||
from urllib.parse import unquote
|
||||
|
||||
import pytest
|
||||
|
||||
from aiofiles import os as async_os
|
||||
|
||||
from sanic.response import (
|
||||
|
@ -23,6 +25,7 @@ from sanic.response import (
|
|||
from sanic.server import HttpProtocol
|
||||
from sanic.testing import HOST, PORT
|
||||
|
||||
|
||||
JSON_DATA = {"ok": True}
|
||||
|
||||
|
||||
|
@ -100,10 +103,14 @@ def test_response_content_length(app):
|
|||
)
|
||||
|
||||
_, response = app.test_client.get("/response_with_space")
|
||||
content_length_for_response_with_space = response.headers.get("Content-Length")
|
||||
content_length_for_response_with_space = response.headers.get(
|
||||
"Content-Length"
|
||||
)
|
||||
|
||||
_, response = app.test_client.get("/response_without_space")
|
||||
content_length_for_response_without_space = response.headers.get("Content-Length")
|
||||
content_length_for_response_without_space = response.headers.get(
|
||||
"Content-Length"
|
||||
)
|
||||
|
||||
assert (
|
||||
content_length_for_response_with_space
|
||||
|
@ -248,7 +255,9 @@ async def test_non_chunked_streaming_adds_correct_headers_asgi(
|
|||
assert response.headers["Content-Length"] == "7"
|
||||
|
||||
|
||||
def test_non_chunked_streaming_returns_correct_content(non_chunked_streaming_app,):
|
||||
def test_non_chunked_streaming_returns_correct_content(
|
||||
non_chunked_streaming_app,
|
||||
):
|
||||
request, response = non_chunked_streaming_app.test_client.get("/")
|
||||
assert response.text == "foo,bar"
|
||||
|
||||
|
@ -261,7 +270,9 @@ def test_stream_response_status_returns_correct_headers(status):
|
|||
|
||||
|
||||
@pytest.mark.parametrize("keep_alive_timeout", [10, 20, 30])
|
||||
def test_stream_response_keep_alive_returns_correct_headers(keep_alive_timeout,):
|
||||
def test_stream_response_keep_alive_returns_correct_headers(
|
||||
keep_alive_timeout,
|
||||
):
|
||||
response = StreamingHTTPResponse(sample_streaming_fn)
|
||||
headers = response.get_headers(
|
||||
keep_alive=True, keep_alive_timeout=keep_alive_timeout
|
||||
|
@ -345,9 +356,13 @@ def test_stream_response_writes_correct_content_to_transport_when_not_chunked(
|
|||
@streaming_app.listener("after_server_start")
|
||||
async def run_stream(app, loop):
|
||||
await response.stream(version="1.0")
|
||||
assert response.protocol.transport.write.call_args_list[1][0][0] == (b"foo,")
|
||||
assert response.protocol.transport.write.call_args_list[1][0][0] == (
|
||||
b"foo,"
|
||||
)
|
||||
|
||||
assert response.protocol.transport.write.call_args_list[2][0][0] == (b"bar")
|
||||
assert response.protocol.transport.write.call_args_list[2][0][0] == (
|
||||
b"bar"
|
||||
)
|
||||
|
||||
assert len(response.protocol.transport.write.call_args_list) == 3
|
||||
|
||||
|
@ -392,7 +407,9 @@ def get_file_content(static_file_directory, file_name):
|
|||
return file.read()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("file_name", ["test.file", "decode me.txt", "python.png"])
|
||||
@pytest.mark.parametrize(
|
||||
"file_name", ["test.file", "decode me.txt", "python.png"]
|
||||
)
|
||||
@pytest.mark.parametrize("status", [200, 401])
|
||||
def test_file_response(app, file_name, static_file_directory, status):
|
||||
@app.route("/files/<filename>", methods=["GET"])
|
||||
|
@ -419,7 +436,9 @@ def test_file_response(app, file_name, static_file_directory, status):
|
|||
("python.png", "logo.png"),
|
||||
],
|
||||
)
|
||||
def test_file_response_custom_filename(app, source, dest, static_file_directory):
|
||||
def test_file_response_custom_filename(
|
||||
app, source, dest, static_file_directory
|
||||
):
|
||||
@app.route("/files/<filename>", methods=["GET"])
|
||||
def file_route(request, filename):
|
||||
file_path = os.path.join(static_file_directory, filename)
|
||||
|
@ -446,7 +465,8 @@ def test_file_head_response(app, file_name, static_file_directory):
|
|||
headers["Content-Length"] = str(stats.st_size)
|
||||
if request.method == "HEAD":
|
||||
return HTTPResponse(
|
||||
headers=headers, content_type=guess_type(file_path)[0] or "text/plain",
|
||||
headers=headers,
|
||||
content_type=guess_type(file_path)[0] or "text/plain",
|
||||
)
|
||||
else:
|
||||
return file(
|
||||
|
@ -464,7 +484,9 @@ def test_file_head_response(app, file_name, static_file_directory):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("file_name", ["test.file", "decode me.txt", "python.png"])
|
||||
@pytest.mark.parametrize(
|
||||
"file_name", ["test.file", "decode me.txt", "python.png"]
|
||||
)
|
||||
def test_file_stream_response(app, file_name, static_file_directory):
|
||||
@app.route("/files/<filename>", methods=["GET"])
|
||||
def file_route(request, filename):
|
||||
|
@ -490,7 +512,9 @@ def test_file_stream_response(app, file_name, static_file_directory):
|
|||
("python.png", "logo.png"),
|
||||
],
|
||||
)
|
||||
def test_file_stream_response_custom_filename(app, source, dest, static_file_directory):
|
||||
def test_file_stream_response_custom_filename(
|
||||
app, source, dest, static_file_directory
|
||||
):
|
||||
@app.route("/files/<filename>", methods=["GET"])
|
||||
def file_route(request, filename):
|
||||
file_path = os.path.join(static_file_directory, filename)
|
||||
|
@ -519,7 +543,8 @@ def test_file_stream_head_response(app, file_name, static_file_directory):
|
|||
stats = await async_os.stat(file_path)
|
||||
headers["Content-Length"] = str(stats.st_size)
|
||||
return HTTPResponse(
|
||||
headers=headers, content_type=guess_type(file_path)[0] or "text/plain",
|
||||
headers=headers,
|
||||
content_type=guess_type(file_path)[0] or "text/plain",
|
||||
)
|
||||
else:
|
||||
return file_stream(
|
||||
|
@ -542,8 +567,12 @@ def test_file_stream_head_response(app, file_name, static_file_directory):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("file_name", ["test.file", "decode me.txt", "python.png"])
|
||||
@pytest.mark.parametrize("size,start,end", [(1024, 0, 1024), (4096, 1024, 8192)])
|
||||
@pytest.mark.parametrize(
|
||||
"file_name", ["test.file", "decode me.txt", "python.png"]
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"size,start,end", [(1024, 0, 1024), (4096, 1024, 8192)]
|
||||
)
|
||||
def test_file_stream_response_range(
|
||||
app, file_name, static_file_directory, size, start, end
|
||||
):
|
||||
|
|
Loading…
Reference in New Issue
Block a user