Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/source/differences-to-vws.rst
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ path which does not start with a served path, such as
For any other request which it does not serve, such as ``DELETE /summary`` or
``GET /targetsfoo``, it gives an HTML "Not Found" page which names the method
and the path of the request.
The Flask and Docker mock gives an empty body for all of these.
The Flask and Docker mock reproduces both response shapes.

The ``requests`` and ``httpx`` backends mock only the paths which the mock
serves, so a request to any other path raises a connection error rather than
Expand Down
37 changes: 32 additions & 5 deletions src/mock_vws/_flask_server/vws.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import base64
import email.utils
import gzip
import html
import json
import logging
import threading
Expand Down Expand Up @@ -401,14 +403,39 @@ def handle_exceptions(exc: ValidatorError) -> Response:
@VWS_FLASK_APP.errorhandler(code_or_exception=HTTPStatus.METHOD_NOT_ALLOWED)
@beartype
def handle_unrouted_request(exc: NotFound | MethodNotAllowed) -> Response:
"""Return a 404 response with no body for a request which no route
serves.
"""Return the real Vuforia 404 shape for an unrouted request.

Real Vuforia returns a 404 response for a request to a path which it does
not serve, and for a request to a served path with a method which that
path does not serve.
Vuforia's edge returns an empty response for unknown path prefixes. Paths
routed to the Cloud Targets application return its HTML Not Found page.
"""
del exc
application_prefixes = ("/duplicates", "/summary", "/targets")
if request.path.startswith(application_prefixes):
request_description = html.escape(
s=f"{request.method} {request.path}",
quote=True,
)
body = f"""<!DOCTYPE html>
<html lang="en">
<head>
<title>Not Found</title>
</head>
<body>
<h1>Not Found</h1>
<p id="detail">For request '{request_description}'</p>
</body>
</html>
""".encode()
return Response(
status=HTTPStatus.NOT_FOUND,
response=gzip.compress(data=body),
headers={
"Content-Type": "text/html; charset=UTF-8",
"Content-Encoding": "gzip",
"x-envoy-upstream-service-time": "5",
},
)

response = Response(status=HTTPStatus.NOT_FOUND, response=b"")
del response.headers["Content-Type"]
return response
Expand Down
26 changes: 25 additions & 1 deletion tests/mock_vws/test_invalid_given_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
there are entries.
"""

import gzip
from dataclasses import dataclass
from http import HTTPMethod, HTTPStatus

Expand Down Expand Up @@ -38,6 +39,8 @@ class _UnroutedResponse:
status_code: int
body: bytes
content_type: str | None
content_encoding: str | None
upstream_service_time: str | None


@beartype
Expand Down Expand Up @@ -77,12 +80,22 @@ def _send_unrouted_request(
method=method,
headers=headers,
)
response_body = test_client_response.data
content_encoding = test_client_response.headers.get(
key="Content-Encoding",
)
if content_encoding == "gzip":
response_body = gzip.decompress(data=response_body)
return _UnroutedResponse(
status_code=test_client_response.status_code,
body=test_client_response.data,
body=response_body,
content_type=test_client_response.headers.get(
key="Content-Type",
),
content_encoding=content_encoding,
upstream_service_time=test_client_response.headers.get(
key="x-envoy-upstream-service-time",
),
)

try:
Expand All @@ -99,6 +112,10 @@ def _send_unrouted_request(
status_code=response.status_code,
body=response.content,
content_type=response.headers.get("Content-Type"),
content_encoding=response.headers.get("Content-Encoding"),
upstream_service_time=response.headers.get(
"x-envoy-upstream-service-time",
),
)


Expand Down Expand Up @@ -171,6 +188,8 @@ def test_unknown_path(
assert response.status_code == HTTPStatus.NOT_FOUND
assert response.body == b""
assert response.content_type is None
assert response.content_encoding is None
assert response.upstream_service_time is None

@staticmethod
def test_unknown_method(
Expand All @@ -194,3 +213,8 @@ def test_unknown_method(

assert response is not None
assert response.status_code == HTTPStatus.NOT_FOUND
assert response.content_type == "text/html; charset=UTF-8"
assert response.content_encoding == "gzip"
assert response.upstream_service_time is not None
assert b"<h1>Not Found</h1>" in response.body
assert b"For request 'DELETE /summary'" in response.body
Loading