Add raw header info to request object (#2032)
This commit is contained in:
parent
c41d7136e8
commit
a733d32715
|
@ -1 +1,2 @@
|
|||
HTTP_METHODS = ("GET", "POST", "PUT", "HEAD", "OPTIONS", "PATCH", "DELETE")
|
||||
DEFAULT_HTTP_CONTENT_TYPE = "application/octet-stream"
|
||||
|
|
|
@ -189,8 +189,9 @@ class Http:
|
|||
|
||||
# Parse header content
|
||||
try:
|
||||
raw_headers = buf[:pos].decode(errors="surrogateescape")
|
||||
reqline, *raw_headers = raw_headers.split("\r\n")
|
||||
head = buf[:pos]
|
||||
raw_headers = head.decode(errors="surrogateescape")
|
||||
reqline, *split_headers = raw_headers.split("\r\n")
|
||||
method, self.url, protocol = reqline.split(" ")
|
||||
|
||||
if protocol == "HTTP/1.1":
|
||||
|
@ -204,7 +205,7 @@ class Http:
|
|||
request_body = False
|
||||
headers = []
|
||||
|
||||
for name, value in (h.split(":", 1) for h in raw_headers):
|
||||
for name, value in (h.split(":", 1) for h in split_headers):
|
||||
name, value = h = name.lower(), value.lstrip()
|
||||
|
||||
if name in ("content-length", "transfer-encoding"):
|
||||
|
@ -223,6 +224,7 @@ class Http:
|
|||
request = self.protocol.request_class(
|
||||
url_bytes=self.url.encode(),
|
||||
headers=headers_instance,
|
||||
head=bytes(head),
|
||||
version=protocol[5:],
|
||||
method=method,
|
||||
transport=self.protocol.transport,
|
||||
|
|
|
@ -11,7 +11,7 @@ from urllib.parse import unquote
|
|||
from sanic_routing.route import Route # type: ignore
|
||||
|
||||
from sanic.compat import stat_async
|
||||
from sanic.constants import HTTP_METHODS
|
||||
from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE, HTTP_METHODS
|
||||
from sanic.exceptions import (
|
||||
ContentRangeError,
|
||||
FileNotFound,
|
||||
|
@ -689,7 +689,7 @@ class RouteMixin:
|
|||
content_type = (
|
||||
content_type
|
||||
or guess_type(file_path)[0]
|
||||
or "application/octet-stream"
|
||||
or DEFAULT_HTTP_CONTENT_TYPE
|
||||
)
|
||||
|
||||
if "charset=" not in content_type and (
|
||||
|
|
|
@ -31,6 +31,7 @@ from urllib.parse import parse_qs, parse_qsl, unquote, urlunparse
|
|||
from httptools import parse_url # type: ignore
|
||||
|
||||
from sanic.compat import CancelledErrors, Header
|
||||
from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE
|
||||
from sanic.exceptions import InvalidUsage
|
||||
from sanic.headers import (
|
||||
Options,
|
||||
|
@ -49,12 +50,6 @@ try:
|
|||
except ImportError:
|
||||
from json import loads as json_loads # type: ignore
|
||||
|
||||
DEFAULT_HTTP_CONTENT_TYPE = "application/octet-stream"
|
||||
|
||||
# HTTP/1.1: https://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
|
||||
# > If the media type remains unknown, the recipient SHOULD treat it
|
||||
# > as type "application/octet-stream"
|
||||
|
||||
|
||||
class RequestParameters(dict):
|
||||
"""
|
||||
|
@ -95,6 +90,7 @@ class Request:
|
|||
"conn_info",
|
||||
"ctx",
|
||||
"endpoint",
|
||||
"head",
|
||||
"headers",
|
||||
"method",
|
||||
"name",
|
||||
|
@ -121,6 +117,7 @@ class Request:
|
|||
method: str,
|
||||
transport: TransportProtocol,
|
||||
app: Sanic,
|
||||
head: bytes = b"",
|
||||
):
|
||||
self.raw_url = url_bytes
|
||||
# TODO: Content-Encoding detection
|
||||
|
@ -132,6 +129,7 @@ class Request:
|
|||
self.version = version
|
||||
self.method = method
|
||||
self.transport = transport
|
||||
self.head = head
|
||||
|
||||
# Init but do not inhale
|
||||
self.body = b""
|
||||
|
@ -207,6 +205,16 @@ class Request:
|
|||
if not self.body:
|
||||
self.body = b"".join([data async for data in self.stream])
|
||||
|
||||
@property
|
||||
def raw_headers(self):
|
||||
_, headers = self.head.split(b"\r\n", 1)
|
||||
return bytes(headers)
|
||||
|
||||
@property
|
||||
def request_line(self):
|
||||
reqline, _ = self.head.split(b"\r\n", 1)
|
||||
return bytes(reqline)
|
||||
|
||||
@property
|
||||
def id(self) -> Optional[Union[uuid.UUID, str, int]]:
|
||||
"""
|
||||
|
|
|
@ -17,6 +17,7 @@ from urllib.parse import quote_plus
|
|||
from warnings import warn
|
||||
|
||||
from sanic.compat import Header, open_async
|
||||
from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE
|
||||
from sanic.cookies import CookieJar
|
||||
from sanic.helpers import has_message_body, remove_entity_headers
|
||||
from sanic.http import Http
|
||||
|
@ -297,7 +298,7 @@ def raw(
|
|||
body: Optional[AnyStr],
|
||||
status: int = 200,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
content_type: str = "application/octet-stream",
|
||||
content_type: str = DEFAULT_HTTP_CONTENT_TYPE,
|
||||
) -> HTTPResponse:
|
||||
"""
|
||||
Returns response object without encoding the body.
|
||||
|
|
|
@ -2,11 +2,9 @@ from unittest.mock import Mock
|
|||
|
||||
import pytest
|
||||
|
||||
from sanic import Sanic, headers
|
||||
from sanic.compat import Header
|
||||
from sanic import headers, text
|
||||
from sanic.exceptions import PayloadTooLarge
|
||||
from sanic.http import Http
|
||||
from sanic.request import Request
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
@ -85,3 +83,35 @@ async def test_header_size_exceeded():
|
|||
|
||||
with pytest.raises(PayloadTooLarge):
|
||||
await http.http1_request_header()
|
||||
|
||||
|
||||
def test_raw_headers(app):
|
||||
app.route("/")(lambda _: text(""))
|
||||
request, _ = app.test_client.get(
|
||||
"/",
|
||||
headers={
|
||||
"FOO": "bar",
|
||||
"Host": "example.com",
|
||||
"User-Agent": "Sanic-Testing",
|
||||
},
|
||||
)
|
||||
|
||||
assert request.raw_headers == (
|
||||
b"Host: example.com\r\nAccept: */*\r\nAccept-Encoding: gzip, "
|
||||
b"deflate\r\nConnection: keep-alive\r\nUser-Agent: "
|
||||
b"Sanic-Testing\r\nFOO: bar"
|
||||
)
|
||||
|
||||
|
||||
def test_request_line(app):
|
||||
app.route("/")(lambda _: text(""))
|
||||
request, _ = app.test_client.get(
|
||||
"/",
|
||||
headers={
|
||||
"FOO": "bar",
|
||||
"Host": "example.com",
|
||||
"User-Agent": "Sanic-Testing",
|
||||
},
|
||||
)
|
||||
|
||||
assert request.request_line == b"GET / HTTP/1.1"
|
||||
|
|
Loading…
Reference in New Issue
Block a user