2021-01-19 02:25:39 +00:00
|
|
|
from unittest.mock import Mock
|
|
|
|
from uuid import UUID, uuid4
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
from sanic import Sanic, response
|
2022-03-24 22:22:12 +00:00
|
|
|
from sanic.exceptions import BadURL
|
2021-01-19 02:25:39 +00:00
|
|
|
from sanic.request import Request, uuid
|
2021-03-17 18:55:52 +00:00
|
|
|
from sanic.server import HttpProtocol
|
2021-01-19 02:25:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_no_request_id_not_called(monkeypatch):
|
|
|
|
monkeypatch.setattr(uuid, "uuid4", Mock())
|
|
|
|
request = Request(b"/", {}, None, "GET", None, None)
|
|
|
|
|
|
|
|
assert request._id is None
|
|
|
|
uuid.uuid4.assert_not_called()
|
|
|
|
|
|
|
|
|
|
|
|
def test_request_id_generates_from_request(monkeypatch):
|
|
|
|
monkeypatch.setattr(Request, "generate_id", Mock())
|
|
|
|
Request.generate_id.return_value = 1
|
|
|
|
request = Request(b"/", {}, None, "GET", None, Mock())
|
2021-04-08 11:30:12 +01:00
|
|
|
request.app.config.REQUEST_ID_HEADER = "foo"
|
2021-01-19 02:25:39 +00:00
|
|
|
|
|
|
|
for _ in range(10):
|
|
|
|
request.id
|
|
|
|
Request.generate_id.assert_called_once_with(request)
|
|
|
|
|
|
|
|
|
|
|
|
def test_request_id_defaults_uuid():
|
|
|
|
request = Request(b"/", {}, None, "GET", None, Mock())
|
2021-04-08 11:30:12 +01:00
|
|
|
request.app.config.REQUEST_ID_HEADER = "foo"
|
2021-01-19 02:25:39 +00:00
|
|
|
|
|
|
|
assert isinstance(request.id, UUID)
|
|
|
|
|
|
|
|
# Makes sure that it has been cached and not called multiple times
|
|
|
|
assert request.id == request.id == request._id
|
|
|
|
|
|
|
|
|
2021-03-21 07:47:21 +00:00
|
|
|
def test_name_none():
|
|
|
|
request = Request(b"/", {}, None, "GET", None, None)
|
|
|
|
|
|
|
|
assert request.name is None
|
|
|
|
|
|
|
|
|
|
|
|
def test_name_from_route():
|
|
|
|
request = Request(b"/", {}, None, "GET", None, None)
|
|
|
|
route = Mock()
|
|
|
|
request.route = route
|
|
|
|
|
|
|
|
assert request.name == route.name
|
|
|
|
|
|
|
|
|
|
|
|
def test_name_from_set():
|
|
|
|
request = Request(b"/", {}, None, "GET", None, None)
|
|
|
|
request._name = "foo"
|
|
|
|
|
|
|
|
assert request.name == "foo"
|
|
|
|
|
|
|
|
|
2021-01-19 02:25:39 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"request_id,expected_type",
|
|
|
|
(
|
|
|
|
(99, int),
|
|
|
|
(uuid4(), UUID),
|
|
|
|
("foo", str),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
def test_request_id(request_id, expected_type):
|
|
|
|
app = Sanic("req-generator")
|
|
|
|
|
|
|
|
@app.get("/")
|
|
|
|
async def get(request):
|
|
|
|
return response.empty()
|
|
|
|
|
|
|
|
request, _ = app.test_client.get(
|
|
|
|
"/", headers={"X-REQUEST-ID": f"{request_id}"}
|
|
|
|
)
|
|
|
|
assert request.id == request_id
|
|
|
|
assert type(request.id) == expected_type
|
|
|
|
|
|
|
|
|
|
|
|
def test_custom_generator():
|
|
|
|
REQUEST_ID = 99
|
|
|
|
|
|
|
|
class FooRequest(Request):
|
|
|
|
@classmethod
|
|
|
|
def generate_id(cls, request):
|
|
|
|
return int(request.headers["some-other-request-id"]) * 2
|
|
|
|
|
|
|
|
app = Sanic("req-generator", request_class=FooRequest)
|
|
|
|
|
|
|
|
@app.get("/")
|
|
|
|
async def get(request):
|
|
|
|
return response.empty()
|
|
|
|
|
|
|
|
request, _ = app.test_client.get(
|
|
|
|
"/", headers={"SOME-OTHER-REQUEST-ID": f"{REQUEST_ID}"}
|
|
|
|
)
|
|
|
|
assert request.id == REQUEST_ID * 2
|
2021-03-01 13:30:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_route_assigned_to_request(app):
|
|
|
|
@app.get("/")
|
|
|
|
async def get(request):
|
|
|
|
return response.empty()
|
|
|
|
|
|
|
|
request, _ = app.test_client.get("/")
|
2021-04-19 22:53:42 +01:00
|
|
|
assert request.route is list(app.router.routes)[0]
|
2021-03-17 18:55:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_protocol_attribute(app):
|
|
|
|
retrieved = None
|
|
|
|
|
|
|
|
@app.get("/")
|
|
|
|
async def get(request):
|
|
|
|
nonlocal retrieved
|
|
|
|
retrieved = request.protocol
|
|
|
|
return response.empty()
|
|
|
|
|
|
|
|
headers = {"Connection": "keep-alive"}
|
|
|
|
_ = app.test_client.get("/", headers=headers)
|
|
|
|
|
|
|
|
assert isinstance(retrieved, HttpProtocol)
|
2021-06-16 20:34:52 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_ipv6_address_is_not_wrapped(app):
|
|
|
|
@app.get("/")
|
|
|
|
async def get(request):
|
|
|
|
return response.json(
|
|
|
|
{
|
|
|
|
"client_ip": request.conn_info.client_ip,
|
|
|
|
"client": request.conn_info.client,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
request, resp = app.test_client.get("/", host="::1")
|
|
|
|
|
|
|
|
assert request.route is list(app.router.routes)[0]
|
|
|
|
assert resp.json["client"] == "[::1]"
|
|
|
|
assert resp.json["client_ip"] == "::1"
|
|
|
|
assert request.ip == "::1"
|
2021-08-19 19:09:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_request_accept():
|
|
|
|
app = Sanic("req-generator")
|
|
|
|
|
|
|
|
@app.get("/")
|
|
|
|
async def get(request):
|
|
|
|
return response.empty()
|
|
|
|
|
|
|
|
request, _ = app.test_client.get(
|
|
|
|
"/",
|
|
|
|
headers={
|
|
|
|
"Accept": "text/*, text/plain, text/plain;format=flowed, */*"
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert request.accept == [
|
|
|
|
"text/plain;format=flowed",
|
|
|
|
"text/plain",
|
|
|
|
"text/*",
|
|
|
|
"*/*",
|
|
|
|
]
|
|
|
|
|
|
|
|
request, _ = app.test_client.get(
|
|
|
|
"/",
|
|
|
|
headers={
|
|
|
|
"Accept": (
|
|
|
|
"text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"
|
|
|
|
)
|
|
|
|
},
|
|
|
|
)
|
|
|
|
assert request.accept == [
|
|
|
|
"text/html",
|
|
|
|
"text/x-c",
|
|
|
|
"text/x-dvi; q=0.8",
|
|
|
|
"text/plain; q=0.5",
|
|
|
|
]
|
2022-03-24 22:22:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_bad_url_parse():
|
|
|
|
message = "Bad URL: my.redacted-domain.com:443"
|
|
|
|
with pytest.raises(BadURL, match=message):
|
|
|
|
Request(
|
|
|
|
b"my.redacted-domain.com:443",
|
|
|
|
Mock(),
|
|
|
|
Mock(),
|
|
|
|
Mock(),
|
|
|
|
Mock(),
|
|
|
|
Mock(),
|
|
|
|
Mock(),
|
|
|
|
)
|
2022-04-26 15:25:29 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_request_scope_raises_exception_when_no_asgi():
|
|
|
|
app = Sanic("no_asgi")
|
|
|
|
|
|
|
|
@app.get("/")
|
|
|
|
async def get(request):
|
|
|
|
return request.scope
|
|
|
|
|
|
|
|
request, response = app.test_client.get("/")
|
|
|
|
assert response.status == 500
|
|
|
|
with pytest.raises(NotImplementedError):
|
|
|
|
_ = request.scope
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_request_scope_is_not_none_when_running_in_asgi(app):
|
|
|
|
@app.get("/")
|
|
|
|
async def get(request):
|
|
|
|
return response.empty()
|
|
|
|
|
|
|
|
request, _ = await app.asgi_client.get("/")
|
|
|
|
|
|
|
|
assert request.scope is not None
|
|
|
|
assert request.scope["method"].lower() == "get"
|
|
|
|
assert request.scope["path"].lower() == "/"
|