More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
import warnings
|
|
|
|
|
2018-11-07 16:02:34 +00:00
|
|
|
from functools import partial
|
2016-10-24 09:21:06 +01:00
|
|
|
from mimetypes import guess_type
|
|
|
|
from os import path
|
2018-07-13 05:31:33 +01:00
|
|
|
from urllib.parse import quote_plus
|
2017-03-28 10:50:09 +01:00
|
|
|
|
2020-01-20 16:29:06 +00:00
|
|
|
from sanic.compat import Header, open_async
|
2017-02-16 02:54:00 +00:00
|
|
|
from sanic.cookies import CookieJar
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
from sanic.headers import format_http1, format_http1_response
|
|
|
|
from sanic.helpers import has_message_body, remove_entity_headers
|
2018-10-18 05:20:16 +01:00
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
from ujson import dumps as json_dumps
|
2019-09-08 22:08:34 +01:00
|
|
|
except ImportError:
|
2018-11-07 16:02:34 +00:00
|
|
|
from json import dumps
|
|
|
|
|
|
|
|
# This is done in order to ensure that the JSON response is
|
|
|
|
# kept consistent across both ujson and inbuilt json usage.
|
|
|
|
json_dumps = partial(dumps, separators=(",", ":"))
|
2016-12-30 18:13:16 +00:00
|
|
|
|
2016-10-15 20:59:00 +01:00
|
|
|
|
2017-02-21 16:05:06 +00:00
|
|
|
class BaseHTTPResponse:
|
|
|
|
def _encode_body(self, data):
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
return data.encode() if hasattr(data, "encode") else data
|
2017-02-21 16:05:06 +00:00
|
|
|
|
|
|
|
def _parse_headers(self):
|
2019-12-23 23:30:45 +00:00
|
|
|
return format_http1(self.headers.items())
|
2017-02-21 16:05:06 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def cookies(self):
|
|
|
|
if self._cookies is None:
|
|
|
|
self._cookies = CookieJar(self.headers)
|
|
|
|
return self._cookies
|
|
|
|
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
def get_headers(
|
|
|
|
self,
|
|
|
|
version="1.1",
|
|
|
|
keep_alive=False,
|
|
|
|
keep_alive_timeout=None,
|
|
|
|
body=b"",
|
|
|
|
):
|
|
|
|
""".. deprecated:: 20.3:
|
|
|
|
This function is not public API and will be removed."""
|
|
|
|
if version != "1.1":
|
|
|
|
warnings.warn(
|
|
|
|
"Only HTTP/1.1 is currently supported (got {version})",
|
|
|
|
DeprecationWarning,
|
|
|
|
)
|
|
|
|
|
|
|
|
# self.headers get priority over content_type
|
|
|
|
if self.content_type and "Content-Type" not in self.headers:
|
|
|
|
self.headers["Content-Type"] = self.content_type
|
|
|
|
|
|
|
|
if keep_alive:
|
|
|
|
self.headers["Connection"] = "keep-alive"
|
|
|
|
if keep_alive_timeout is not None:
|
|
|
|
self.headers["Keep-Alive"] = keep_alive_timeout
|
|
|
|
else:
|
|
|
|
self.headers["Connection"] = "close"
|
|
|
|
|
|
|
|
if self.status in (304, 412):
|
|
|
|
self.headers = remove_entity_headers(self.headers)
|
|
|
|
|
|
|
|
return format_http1_response(self.status, self.headers.items(), body)
|
|
|
|
|
2017-02-21 16:05:06 +00:00
|
|
|
|
|
|
|
class StreamingHTTPResponse(BaseHTTPResponse):
|
|
|
|
__slots__ = (
|
2018-10-14 01:55:33 +01:00
|
|
|
"protocol",
|
|
|
|
"streaming_fn",
|
|
|
|
"status",
|
|
|
|
"content_type",
|
|
|
|
"headers",
|
2019-04-20 20:26:30 +01:00
|
|
|
"chunked",
|
2018-10-14 01:55:33 +01:00
|
|
|
"_cookies",
|
2017-07-26 17:32:23 +01:00
|
|
|
)
|
2017-02-21 16:05:06 +00:00
|
|
|
|
2018-10-14 01:55:33 +01:00
|
|
|
def __init__(
|
2019-04-20 20:26:30 +01:00
|
|
|
self,
|
|
|
|
streaming_fn,
|
|
|
|
status=200,
|
|
|
|
headers=None,
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
content_type="text/plain; charset=utf-8",
|
2019-04-22 08:52:38 +01:00
|
|
|
chunked=True,
|
2018-10-14 01:55:33 +01:00
|
|
|
):
|
2017-02-21 16:05:06 +00:00
|
|
|
self.content_type = content_type
|
|
|
|
self.streaming_fn = streaming_fn
|
|
|
|
self.status = status
|
2019-07-19 04:11:25 +01:00
|
|
|
self.headers = Header(headers or {})
|
2019-04-20 20:26:30 +01:00
|
|
|
self.chunked = chunked
|
2017-02-21 16:05:06 +00:00
|
|
|
self._cookies = None
|
|
|
|
|
2018-08-19 02:12:13 +01:00
|
|
|
async def write(self, data):
|
2017-02-21 16:05:06 +00:00
|
|
|
"""Writes a chunk of data to the streaming response.
|
|
|
|
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
:param data: str or bytes-ish data to be written.
|
2017-02-21 16:05:06 +00:00
|
|
|
"""
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
data = self._encode_body(data)
|
2017-02-22 15:42:16 +00:00
|
|
|
|
2019-04-20 20:26:30 +01:00
|
|
|
if self.chunked:
|
2019-05-27 00:11:52 +01:00
|
|
|
await self.protocol.push_data(b"%x\r\n%b\r\n" % (len(data), data))
|
2019-04-20 20:26:30 +01:00
|
|
|
else:
|
2019-05-27 00:11:52 +01:00
|
|
|
await self.protocol.push_data(data)
|
2018-08-19 02:12:13 +01:00
|
|
|
await self.protocol.drain()
|
2017-02-21 16:05:06 +00:00
|
|
|
|
|
|
|
async def stream(
|
2018-10-14 01:55:33 +01:00
|
|
|
self, version="1.1", keep_alive=False, keep_alive_timeout=None
|
|
|
|
):
|
2017-03-29 04:44:01 +01:00
|
|
|
"""Streams headers, runs the `streaming_fn` callback that writes
|
|
|
|
content to the response body, then finalizes the response body.
|
2017-02-21 16:05:06 +00:00
|
|
|
"""
|
2019-04-22 08:52:38 +01:00
|
|
|
if version != "1.1":
|
|
|
|
self.chunked = False
|
2017-02-21 16:05:06 +00:00
|
|
|
headers = self.get_headers(
|
2018-10-14 01:55:33 +01:00
|
|
|
version,
|
|
|
|
keep_alive=keep_alive,
|
|
|
|
keep_alive_timeout=keep_alive_timeout,
|
|
|
|
)
|
2019-05-27 00:11:52 +01:00
|
|
|
await self.protocol.push_data(headers)
|
2018-08-19 02:12:13 +01:00
|
|
|
await self.protocol.drain()
|
2017-02-21 16:05:06 +00:00
|
|
|
await self.streaming_fn(self)
|
2019-04-20 20:26:30 +01:00
|
|
|
if self.chunked:
|
2019-05-27 00:11:52 +01:00
|
|
|
await self.protocol.push_data(b"0\r\n\r\n")
|
2018-08-19 02:12:13 +01:00
|
|
|
# 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.
|
2017-02-21 16:05:06 +00:00
|
|
|
|
|
|
|
def get_headers(
|
2018-10-14 01:55:33 +01:00
|
|
|
self, version="1.1", keep_alive=False, keep_alive_timeout=None
|
|
|
|
):
|
2019-04-22 08:52:38 +01:00
|
|
|
if self.chunked and version == "1.1":
|
2019-04-20 20:26:30 +01:00
|
|
|
self.headers["Transfer-Encoding"] = "chunked"
|
|
|
|
self.headers.pop("Content-Length", None)
|
2017-02-21 16:05:06 +00:00
|
|
|
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
return super().get_headers(version, keep_alive, keep_alive_timeout)
|
2017-02-21 16:05:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
class HTTPResponse(BaseHTTPResponse):
|
2018-10-14 01:55:33 +01:00
|
|
|
__slots__ = ("body", "status", "content_type", "headers", "_cookies")
|
|
|
|
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
body=None,
|
|
|
|
status=200,
|
|
|
|
headers=None,
|
2019-12-23 20:16:53 +00:00
|
|
|
content_type=None,
|
2018-10-14 01:55:33 +01:00
|
|
|
body_bytes=b"",
|
|
|
|
):
|
2016-10-15 20:59:00 +01:00
|
|
|
self.content_type = content_type
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
self.body = body_bytes if body is None else self._encode_body(body)
|
2016-10-15 20:59:00 +01:00
|
|
|
self.status = status
|
2019-07-19 04:11:25 +01:00
|
|
|
self.headers = Header(headers or {})
|
2016-10-23 09:32:16 +01:00
|
|
|
self._cookies = None
|
2016-10-15 20:59:00 +01:00
|
|
|
|
2018-10-14 01:55:33 +01:00
|
|
|
def output(self, version="1.1", keep_alive=False, keep_alive_timeout=None):
|
|
|
|
body = b""
|
2018-09-25 18:46:40 +01:00
|
|
|
if has_message_body(self.status):
|
2018-02-02 08:29:54 +00:00
|
|
|
body = self.body
|
2018-10-14 01:55:33 +01:00
|
|
|
self.headers["Content-Length"] = self.headers.get(
|
|
|
|
"Content-Length", len(self.body)
|
|
|
|
)
|
2018-02-01 16:51:51 +00:00
|
|
|
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
return self.get_headers(version, keep_alive, keep_alive_timeout, body)
|
2016-10-15 20:59:00 +01:00
|
|
|
|
2016-10-23 09:32:16 +01:00
|
|
|
@property
|
|
|
|
def cookies(self):
|
|
|
|
if self._cookies is None:
|
2016-10-25 09:27:54 +01:00
|
|
|
self._cookies = CookieJar(self.headers)
|
2016-10-23 09:32:16 +01:00
|
|
|
return self._cookies
|
|
|
|
|
2016-10-16 10:21:24 +01:00
|
|
|
|
2020-01-11 03:58:01 +00:00
|
|
|
def empty(status=204, headers=None):
|
2019-12-23 20:16:53 +00:00
|
|
|
"""
|
|
|
|
Returns an empty response to the client.
|
|
|
|
|
|
|
|
:param status Response code.
|
|
|
|
:param headers Custom Headers.
|
|
|
|
"""
|
2020-01-11 03:58:01 +00:00
|
|
|
return HTTPResponse(body_bytes=b"", status=status, headers=headers)
|
2019-12-23 20:16:53 +00:00
|
|
|
|
|
|
|
|
2018-10-14 01:55:33 +01:00
|
|
|
def json(
|
|
|
|
body,
|
|
|
|
status=200,
|
|
|
|
headers=None,
|
|
|
|
content_type="application/json",
|
|
|
|
dumps=json_dumps,
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
**kwargs,
|
2018-10-14 01:55:33 +01:00
|
|
|
):
|
2016-12-25 06:05:26 +00:00
|
|
|
"""
|
2016-12-25 06:24:17 +00:00
|
|
|
Returns response object with body in json format.
|
2017-07-02 07:46:34 +01:00
|
|
|
|
2016-12-25 06:05:26 +00:00
|
|
|
:param body: Response data to be serialized.
|
|
|
|
:param status: Response code.
|
|
|
|
:param headers: Custom Headers.
|
2017-01-31 01:04:51 +00:00
|
|
|
:param kwargs: Remaining arguments that are passed to the json encoder.
|
2016-12-25 06:05:26 +00:00
|
|
|
"""
|
2018-10-14 01:55:33 +01:00
|
|
|
return HTTPResponse(
|
|
|
|
dumps(body, **kwargs),
|
|
|
|
headers=headers,
|
|
|
|
status=status,
|
|
|
|
content_type=content_type,
|
|
|
|
)
|
2016-10-15 20:59:00 +01:00
|
|
|
|
|
|
|
|
2018-10-14 01:55:33 +01:00
|
|
|
def text(
|
|
|
|
body, status=200, headers=None, content_type="text/plain; charset=utf-8"
|
|
|
|
):
|
2016-12-25 06:05:26 +00:00
|
|
|
"""
|
2016-12-25 06:24:17 +00:00
|
|
|
Returns response object with body in text format.
|
2017-07-02 07:46:34 +01:00
|
|
|
|
2016-12-25 06:05:26 +00:00
|
|
|
:param body: Response data to be encoded.
|
|
|
|
:param status: Response code.
|
|
|
|
:param headers: Custom Headers.
|
2017-03-16 05:52:18 +00:00
|
|
|
:param content_type: the content type (string) of the response
|
2016-12-25 06:05:26 +00:00
|
|
|
"""
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
if not isinstance(body, str):
|
|
|
|
warnings.warn(
|
|
|
|
"Types other than str will be deprecated in future versions for"
|
|
|
|
f" response.text, got type {type(body).__name__})",
|
|
|
|
DeprecationWarning,
|
|
|
|
)
|
|
|
|
# Type conversions are deprecated and quite b0rked but still supported for
|
|
|
|
# text() until applications get fixed. This try-except should be removed.
|
|
|
|
try:
|
|
|
|
# Avoid repr(body).encode() b0rkage for body that is already encoded.
|
|
|
|
# memoryview used only to test bytes-ishness.
|
|
|
|
with memoryview(body):
|
|
|
|
pass
|
|
|
|
except TypeError:
|
|
|
|
body = f"{body}" # no-op if body is already str
|
2017-02-21 16:05:06 +00:00
|
|
|
return HTTPResponse(
|
2018-10-14 01:55:33 +01:00
|
|
|
body, status=status, headers=headers, content_type=content_type
|
|
|
|
)
|
2017-02-14 17:27:39 +00:00
|
|
|
|
|
|
|
|
2018-10-14 01:55:33 +01:00
|
|
|
def raw(
|
|
|
|
body, status=200, headers=None, content_type="application/octet-stream"
|
|
|
|
):
|
2017-02-14 17:27:39 +00:00
|
|
|
"""
|
2017-02-14 17:40:33 +00:00
|
|
|
Returns response object without encoding the body.
|
2017-07-02 07:46:34 +01:00
|
|
|
|
2017-02-14 17:40:33 +00:00
|
|
|
:param body: Response data.
|
2017-02-14 17:27:39 +00:00
|
|
|
:param status: Response code.
|
|
|
|
:param headers: Custom Headers.
|
2017-03-16 05:52:18 +00:00
|
|
|
:param content_type: the content type (string) of the response.
|
2017-02-14 17:27:39 +00:00
|
|
|
"""
|
2018-10-14 01:55:33 +01:00
|
|
|
return HTTPResponse(
|
|
|
|
body_bytes=body,
|
|
|
|
status=status,
|
|
|
|
headers=headers,
|
|
|
|
content_type=content_type,
|
|
|
|
)
|
2016-10-15 20:59:00 +01:00
|
|
|
|
|
|
|
|
|
|
|
def html(body, status=200, headers=None):
|
2016-12-25 06:05:26 +00:00
|
|
|
"""
|
2016-12-25 06:24:17 +00:00
|
|
|
Returns response object with body in html format.
|
2017-07-02 07:46:34 +01:00
|
|
|
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
:param body: str or bytes-ish, or an object with __html__ or _repr_html_.
|
2016-12-25 06:05:26 +00:00
|
|
|
:param status: Response code.
|
|
|
|
:param headers: Custom Headers.
|
|
|
|
"""
|
More robust response datatype handling (#1674)
* HTTP1 header formatting moved to headers.format_headers and rewritten.
- New implementation is one line of code and twice faster than the old one.
- Whole header block encoded to UTF-8 in one pass.
- No longer supports custom encode method on header values.
- Cookie objects now have __str__ in addition to encode, to work with this.
* Linter
* format_http1_response
* Replace encode_body with faster implementation based on f-string.
Benchmarks:
def encode_body(data):
try:
# Try to encode it regularly
return data.encode()
except AttributeError:
# Convert it to a str if you can't
return str(data).encode()
def encode_body2(data):
return f"{data}".encode()
def encode_body3(data):
return str(data).encode()
data_str, data_int = "foo", 123
%timeit encode_body(data_int)
928 ns ± 2.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_int)
280 ns ± 2.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body3(data_int)
387 ns ± 1.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body(data_str)
202 ns ± 1.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit encode_body2(data_str)
197 ns ± 0.507 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit encode_body3(data_str)
313 ns ± 1.28 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
* Wtf linter
* Content-type fixes.
* Body encoding sanitation, first pass.
- body/data type autodetection fixed.
- do not repr(body).encode() bytes-ish values.
- support __html__ and _repr_html_ in sanic.response.html().
* <any type>-to-str response autoconversion limited to sanic.response.text() only.
* Workaround MyPy issue.
* Add an empty line to make isort happy.
* Add html test for __html__ and _repr_html_.
* Remove StreamingHTTPResponse.get_headers helper function.
* Add back HTTPResponse Keep-Alive removed by earlier merge or something.
* Revert "Remove StreamingHTTPResponse.get_headers helper function."
Tests depend on this otherwise useless function.
This reverts commit 9651e6ae017b61bed6dd88af6631cdd6b01eb347.
* Add deprecation warnings; instead of assert for wrong HTTP version, and for non-string response.text.
* Add back missing import.
* Avoid duplicate response header tweaking code.
* Linter errors
2020-01-20 16:34:32 +00:00
|
|
|
if hasattr(body, "__html__"):
|
|
|
|
body = body.__html__()
|
|
|
|
elif hasattr(body, "_repr_html_"):
|
|
|
|
body = body._repr_html_()
|
2018-10-14 01:55:33 +01:00
|
|
|
return HTTPResponse(
|
|
|
|
body,
|
|
|
|
status=status,
|
|
|
|
headers=headers,
|
|
|
|
content_type="text/html; charset=utf-8",
|
|
|
|
)
|
2016-10-24 09:21:06 +01:00
|
|
|
|
|
|
|
|
2018-10-14 01:55:33 +01:00
|
|
|
async def file(
|
|
|
|
location,
|
|
|
|
status=200,
|
|
|
|
mime_type=None,
|
|
|
|
headers=None,
|
|
|
|
filename=None,
|
|
|
|
_range=None,
|
|
|
|
):
|
2017-02-14 19:10:19 +00:00
|
|
|
"""Return a response object with file data.
|
|
|
|
|
2016-12-25 06:05:26 +00:00
|
|
|
:param location: Location of file on system.
|
|
|
|
:param mime_type: Specific mime_type.
|
|
|
|
:param headers: Custom Headers.
|
2017-10-09 15:45:22 +01:00
|
|
|
:param filename: Override filename.
|
2017-01-31 01:04:51 +00:00
|
|
|
:param _range:
|
2016-12-25 06:05:26 +00:00
|
|
|
"""
|
2017-10-09 15:45:22 +01:00
|
|
|
headers = headers or {}
|
|
|
|
if filename:
|
|
|
|
headers.setdefault(
|
2020-01-27 06:08:34 +00:00
|
|
|
"Content-Disposition", f'attachment; filename="{filename}"'
|
2018-10-14 01:55:33 +01:00
|
|
|
)
|
2017-10-09 15:45:22 +01:00
|
|
|
filename = filename or path.split(location)[-1]
|
2016-10-24 09:21:06 +01:00
|
|
|
|
2020-01-27 06:08:34 +00:00
|
|
|
async with await open_async(location, mode="rb") as f:
|
2017-01-31 01:04:51 +00:00
|
|
|
if _range:
|
2020-01-27 06:08:34 +00:00
|
|
|
await f.seek(_range.start)
|
|
|
|
out_stream = await f.read(_range.size)
|
|
|
|
headers[
|
|
|
|
"Content-Range"
|
|
|
|
] = "bytes {0.start}-{0.end}/{0.total}".format(_range)
|
2018-11-07 13:36:56 +00:00
|
|
|
status = 206
|
2017-01-31 01:04:51 +00:00
|
|
|
else:
|
2020-01-27 06:08:34 +00:00
|
|
|
out_stream = await f.read()
|
2016-10-24 09:21:06 +01:00
|
|
|
|
2018-10-14 01:55:33 +01:00
|
|
|
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,
|
|
|
|
)
|
2017-01-14 05:41:54 +00:00
|
|
|
|
|
|
|
|
2018-10-14 01:55:33 +01:00
|
|
|
async def file_stream(
|
|
|
|
location,
|
|
|
|
status=200,
|
|
|
|
chunk_size=4096,
|
|
|
|
mime_type=None,
|
|
|
|
headers=None,
|
|
|
|
filename=None,
|
2019-04-22 08:52:38 +01:00
|
|
|
chunked=True,
|
2018-10-14 01:55:33 +01:00
|
|
|
_range=None,
|
|
|
|
):
|
2017-05-18 09:04:28 +01:00
|
|
|
"""Return a streaming response object with file data.
|
|
|
|
|
|
|
|
:param location: Location of file on system.
|
|
|
|
:param chunk_size: The size of each chunk in the stream (in bytes)
|
|
|
|
:param mime_type: Specific mime_type.
|
|
|
|
:param headers: Custom Headers.
|
2017-10-09 15:45:22 +01:00
|
|
|
:param filename: Override filename.
|
2019-04-22 08:52:38 +01:00
|
|
|
:param chunked: Enable or disable chunked transfer-encoding
|
2017-05-18 09:04:28 +01:00
|
|
|
:param _range:
|
|
|
|
"""
|
2017-10-09 15:45:22 +01:00
|
|
|
headers = headers or {}
|
|
|
|
if filename:
|
|
|
|
headers.setdefault(
|
2020-01-27 06:08:34 +00:00
|
|
|
"Content-Disposition", f'attachment; filename="{filename}"'
|
2018-10-14 01:55:33 +01:00
|
|
|
)
|
2017-10-09 15:45:22 +01:00
|
|
|
filename = filename or path.split(location)[-1]
|
2020-01-27 06:08:34 +00:00
|
|
|
mime_type = mime_type or guess_type(filename)[0] or "text/plain"
|
|
|
|
if _range:
|
|
|
|
headers["Content-Range"] = "bytes {0.start}-{0.end}/{0.total}".format(
|
|
|
|
_range
|
|
|
|
)
|
|
|
|
status = 206
|
2017-05-18 09:04:28 +01:00
|
|
|
|
|
|
|
async def _streaming_fn(response):
|
2020-01-27 06:08:34 +00:00
|
|
|
async with await open_async(location, mode="rb") as f:
|
2017-05-18 09:04:28 +01:00
|
|
|
if _range:
|
2020-01-27 06:08:34 +00:00
|
|
|
await f.seek(_range.start)
|
2017-05-18 09:04:28 +01:00
|
|
|
to_send = _range.size
|
|
|
|
while to_send > 0:
|
2020-01-27 06:08:34 +00:00
|
|
|
content = await f.read(min((_range.size, chunk_size)))
|
2017-05-18 09:04:28 +01:00
|
|
|
if len(content) < 1:
|
|
|
|
break
|
|
|
|
to_send -= len(content)
|
2018-08-19 02:12:13 +01:00
|
|
|
await response.write(content)
|
2017-05-18 09:04:28 +01:00
|
|
|
else:
|
|
|
|
while True:
|
2020-01-27 06:08:34 +00:00
|
|
|
content = await f.read(chunk_size)
|
2017-05-18 09:04:28 +01:00
|
|
|
if len(content) < 1:
|
|
|
|
break
|
2018-08-19 02:12:13 +01:00
|
|
|
await response.write(content)
|
2017-05-18 09:04:28 +01:00
|
|
|
|
2018-10-14 01:55:33 +01:00
|
|
|
return StreamingHTTPResponse(
|
|
|
|
streaming_fn=_streaming_fn,
|
|
|
|
status=status,
|
|
|
|
headers=headers,
|
|
|
|
content_type=mime_type,
|
2019-04-20 20:26:30 +01:00
|
|
|
chunked=chunked,
|
2018-10-14 01:55:33 +01:00
|
|
|
)
|
2017-05-18 09:04:28 +01:00
|
|
|
|
|
|
|
|
2017-02-21 16:05:06 +00:00
|
|
|
def stream(
|
2018-10-14 01:55:33 +01:00
|
|
|
streaming_fn,
|
|
|
|
status=200,
|
|
|
|
headers=None,
|
|
|
|
content_type="text/plain; charset=utf-8",
|
2019-04-22 08:52:38 +01:00
|
|
|
chunked=True,
|
2018-10-14 01:55:33 +01:00
|
|
|
):
|
2017-02-21 16:05:06 +00:00
|
|
|
"""Accepts an coroutine `streaming_fn` which can be used to
|
|
|
|
write chunks to a streaming response. Returns a `StreamingHTTPResponse`.
|
|
|
|
|
2017-03-16 05:52:18 +00:00
|
|
|
Example usage::
|
|
|
|
|
|
|
|
@app.route("/")
|
|
|
|
async def index(request):
|
|
|
|
async def streaming_fn(response):
|
|
|
|
await response.write('foo')
|
|
|
|
await response.write('bar')
|
2017-02-21 16:05:06 +00:00
|
|
|
|
2017-03-16 05:52:18 +00:00
|
|
|
return stream(streaming_fn, content_type='text/plain')
|
2017-02-21 16:05:06 +00:00
|
|
|
|
|
|
|
:param streaming_fn: A coroutine accepts a response and
|
|
|
|
writes content to that response.
|
|
|
|
:param mime_type: Specific mime_type.
|
|
|
|
:param headers: Custom Headers.
|
2019-04-22 08:52:38 +01:00
|
|
|
:param chunked: Enable or disable chunked transfer-encoding
|
2017-02-21 16:05:06 +00:00
|
|
|
"""
|
|
|
|
return StreamingHTTPResponse(
|
2019-04-20 20:26:30 +01:00
|
|
|
streaming_fn,
|
|
|
|
headers=headers,
|
|
|
|
content_type=content_type,
|
|
|
|
status=status,
|
|
|
|
chunked=chunked,
|
2017-03-29 04:47:52 +01:00
|
|
|
)
|
2017-02-21 16:05:06 +00:00
|
|
|
|
|
|
|
|
2018-10-14 01:55:33 +01:00
|
|
|
def redirect(
|
|
|
|
to, headers=None, status=302, content_type="text/html; charset=utf-8"
|
|
|
|
):
|
2017-02-14 19:10:19 +00:00
|
|
|
"""Abort execution and cause a 302 redirect (by default).
|
2017-01-14 05:41:54 +00:00
|
|
|
|
|
|
|
:param to: path or fully qualified URL to redirect to
|
|
|
|
:param headers: optional dict of headers to include in the new request
|
|
|
|
:param status: status code (int) of the new request, defaults to 302
|
2017-02-14 19:10:19 +00:00
|
|
|
:param content_type: the content type (string) of the response
|
2017-01-14 05:41:54 +00:00
|
|
|
:returns: the redirecting Response
|
|
|
|
"""
|
|
|
|
headers = headers or {}
|
|
|
|
|
2018-07-13 05:31:33 +01:00
|
|
|
# URL Quote the URL before redirecting
|
2018-10-16 05:53:11 +01:00
|
|
|
safe_to = quote_plus(to, safe=":/%#?&=@[]!$&'()*+,;")
|
2018-07-13 05:31:33 +01:00
|
|
|
|
2017-01-14 05:41:54 +00:00
|
|
|
# According to RFC 7231, a relative URI is now permitted.
|
2018-10-14 01:55:33 +01:00
|
|
|
headers["Location"] = safe_to
|
2017-01-14 05:41:54 +00:00
|
|
|
|
|
|
|
return HTTPResponse(
|
2018-10-14 01:55:33 +01:00
|
|
|
status=status, headers=headers, content_type=content_type
|
|
|
|
)
|