-
Notifications
You must be signed in to change notification settings - Fork 557
Closed
Description
How do you use Sentry?
Sentry Saas (sentry.io)
Version
2.38.0
Steps to Reproduce
Sentry FastAPI middleware hangs if user middleware raises an exception. If I comment out sentry_sdk.init()
call, I get the correct 422 response.
fastapi==0.115.12
starlette==0.46.2
import gzip
import json
import typing
import zlib
import sentry_sdk
from fastapi import FastAPI, Request
from fastapi.testclient import TestClient
from sentry_sdk.integrations.fastapi import FastApiIntegration
from sentry_sdk.integrations.starlette import StarletteIntegration
from starlette import status
from starlette.datastructures import Headers
from starlette.exceptions import HTTPException
from starlette.types import ASGIApp, Message, Receive, Scope, Send
class GZipRequestMiddleware:
def __init__(self, app: ASGIApp):
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] == "http":
headers = Headers(scope=scope)
if headers.get("Content-Encoding") == "gzip":
reader = GZipRequestReader(self.app)
await reader(scope, receive, send)
return
await self.app(scope, receive, send)
class GZipRequestReader:
def __init__(self, app: ASGIApp) -> None:
self.app = app
self.receive: Receive = unattached_receive
self.scope: Scope = {}
self.actual_body_size = 0
self.decompressor = zlib.decompressobj(16 + zlib.MAX_WBITS)
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
self.scope = scope
self.receive = receive
await self.app(scope, self._receive_gzipped_request, send)
async def _receive_gzipped_request(self) -> Message:
message = await self.receive()
body = message.get("body", b"")
if body:
try:
message["body"] = self.decompressor.decompress(body)
except zlib.error as e:
raise HTTPException(
status.HTTP_422_UNPROCESSABLE_ENTITY, "Compressed request body is malformed!"
) from e
else:
self.actual_body_size += len(message["body"])
if not message.get("more_body", False):
if not self.decompressor.eof:
raise HTTPException(
status.HTTP_422_UNPROCESSABLE_ENTITY, "Compressed request body is truncated or incomplete!"
)
headers_copy = Headers(scope=self.scope).mutablecopy()
del headers_copy["Content-Encoding"]
headers_copy["Content-Length"] = str(self.actual_body_size)
self.scope["headers"] = headers_copy.raw
return message
async def unattached_receive() -> typing.NoReturn:
raise RuntimeError("awaitable not set") # noqa: EM101, TRY003 # pragma: no cover
if __name__ == "__main__":
sentry_sdk.init(
dsn="",
send_default_pii=True,
traces_sample_rate=1.0,
environment="production",
integrations=[
FastApiIntegration(failed_request_status_codes={*range(500, 600)}),
StarletteIntegration(failed_request_status_codes={*range(500, 600)}),
],
)
app = FastAPI()
app.add_middleware(GZipRequestMiddleware)
@app.post("/test")
async def test(request: Request):
print(await request.body())
return await request.body()
test_client = TestClient(app)
uncompressed_body = '{"test": "test"}' # Invalid uncompressed body. Should return 422
response = test_client.post(
"/test", content=uncompressed_body, headers={"Content-Encoding": "gzip", "Content-Type": "application/json"}
)
print(response.status_code)
print(response.text)
Expected Result
Request should not hang and return 422 with "Compressed request body is malformed!" error message.
Actual Result
Cannot get response or timeout exception. It just hangs indefinitely.
Metadata
Metadata
Assignees
Projects
Status
No status