format_http1_response

This commit is contained in:
L. Kärkkäinen 2019-09-05 11:43:23 +03:00
parent d248dbb72b
commit 7dc683913f
2 changed files with 35 additions and 45 deletions

View File

@ -3,6 +3,8 @@ import re
from typing import Any, Dict, Iterable, Optional, Tuple from typing import Any, Dict, Iterable, Optional, Tuple
from urllib.parse import unquote from urllib.parse import unquote
from sanic.helpers import STATUS_CODES
HeaderIterable = Iterable[Tuple[str, Any]] # Values convertible to str HeaderIterable = Iterable[Tuple[str, Any]] # Values convertible to str
Options = Dict[str, str] # key=value fields in various headers Options = Dict[str, str] # key=value fields in various headers
@ -175,3 +177,21 @@ def format_http1(headers: HeaderIterable) -> bytes:
- Values are converted into strings if necessary. - Values are converted into strings if necessary.
""" """
return "".join(f"{name}: {val}\r\n" for name, val in headers).encode() return "".join(f"{name}: {val}\r\n" for name, val in headers).encode()
def format_http1_response(
status: int, headers: HeaderIterable, body=b""
) -> bytes:
"""Format a full HTTP/1.1 response.
- If `body` is included, content-length must be specified in headers.
"""
headers = format_http1(headers)
if status == 200:
return b"HTTP/1.1 200 OK\r\n%b\r\n%b" % (headers, body)
return b"HTTP/1.1 %d %b\r\n%b\r\n%b" % (
status,
STATUS_CODES.get(status, b"UNKNOWN"),
headers,
body,
)

View File

@ -7,8 +7,8 @@ from aiofiles import open as open_async
from sanic.compat import Header from sanic.compat import Header
from sanic.cookies import CookieJar from sanic.cookies import CookieJar
from sanic.headers import format_http1 from sanic.headers import format_http1, format_http1_response
from sanic.helpers import STATUS_CODES, has_message_body, remove_entity_headers from sanic.helpers import has_message_body, remove_entity_headers
try: try:
@ -104,33 +104,17 @@ class StreamingHTTPResponse(BaseHTTPResponse):
def get_headers( def get_headers(
self, version="1.1", keep_alive=False, keep_alive_timeout=None self, version="1.1", keep_alive=False, keep_alive_timeout=None
): ):
# This is all returned in a kind-of funky way if "Content-Type" not in self.headers:
# We tried to make this as fast as possible in pure python self.headers["Content-Type"] = self.content_type
timeout_header = b""
if keep_alive and keep_alive_timeout is not None: if keep_alive and keep_alive_timeout is not None:
timeout_header = b"Keep-Alive: %d\r\n" % keep_alive_timeout self.headers["Keep-Alive"] = keep_alive_timeout
if self.chunked and version == "1.1": if self.chunked and version == "1.1":
self.headers["Transfer-Encoding"] = "chunked" self.headers["Transfer-Encoding"] = "chunked"
self.headers.pop("Content-Length", None) self.headers.pop("Content-Length", None)
self.headers["Content-Type"] = self.headers.get(
"Content-Type", self.content_type
)
headers = self._parse_headers() return format_http1_response(self.status, self.headers.items())
if self.status == 200:
status = b"OK"
else:
status = STATUS_CODES.get(self.status)
return (b"HTTP/%b %d %b\r\n" b"%b" b"%b\r\n") % (
version.encode(),
self.status,
status,
timeout_header,
headers,
)
class HTTPResponse(BaseHTTPResponse): class HTTPResponse(BaseHTTPResponse):
@ -156,11 +140,8 @@ class HTTPResponse(BaseHTTPResponse):
self._cookies = None self._cookies = None
def output(self, version="1.1", keep_alive=False, keep_alive_timeout=None): def output(self, version="1.1", keep_alive=False, keep_alive_timeout=None):
# This is all returned in a kind-of funky way if "Content-Type" not in self.headers:
# We tried to make this as fast as possible in pure python self.headers["Content-Type"] = self.content_type
timeout_header = b""
if keep_alive and keep_alive_timeout is not None:
timeout_header = b"Keep-Alive: %d\r\n" % keep_alive_timeout
body = b"" body = b""
if has_message_body(self.status): if has_message_body(self.status):
@ -176,24 +157,13 @@ class HTTPResponse(BaseHTTPResponse):
if self.status in (304, 412): if self.status in (304, 412):
self.headers = remove_entity_headers(self.headers) self.headers = remove_entity_headers(self.headers)
headers = self._parse_headers() if keep_alive and keep_alive_timeout is not None:
self.headers["Connection"] = "keep-alive"
self.headers["Keep-Alive"] = keep_alive_timeout
elif not keep_alive:
self.headers["Connection"] = "close"
if self.status == 200: return format_http1_response(self.status, self.headers.items(), body)
status = b"OK"
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"
) % (
version.encode(),
self.status,
status,
b"keep-alive" if keep_alive else b"close",
timeout_header,
headers,
body,
)
@property @property
def cookies(self): def cookies(self):