dynatrace-oss / OneAgent-SDK-Python-AutoInstrumentation

autodynatrace, a python library that implements automatic instrumentation using the OneAgent SDK for Python
Other
62 stars 28 forks source link

FastAPI wrapper fails for requests from a unix socket. #103

Open theo-fokkinga opened 2 months ago

theo-fokkinga commented 2 months ago

Describe the bug We use a setup with a Gunicorn/FastAPI application binding to a unix socket. The FastAPI middleware from autodynatrace gives a TypeError: 'NoneType' object is not subscriptable on the scope["server"] variable.

Traceback (most recent call last):
  File "/oracle_sdi/venv/lib64/python3.11/site-packages/uvicorn/protocols/http/h11_impl.py", line 428, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/oracle_sdi/venv/lib64/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/oracle_sdi/venv/lib64/python3.11/site-packages/fastapi/applications.py", line 1106, in __call__
    await super().__call__(scope, receive, send)
  File "/oracle_sdi/venv/lib64/python3.11/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/oracle_sdi/venv/lib64/python3.11/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/oracle_sdi/venv/lib64/python3.11/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/oracle_sdi/venv/lib64/python3.11/site-packages/autodynatrace/wrappers/fastapi/middleware.py", line 26, in __call__
    host = "{}:{}".format(scope["server"][0], scope["server"][1])
                          ~~~~~~~~~~~~~~~^^^
TypeError: 'NoneType' object is not subscriptable

To Reproduce Steps to reproduce the behavior:

  1. Configure Nginx to pass the requests to a unix socket
  2. Configure Gunicorn to bind to unix socket
  3. Configure Gunicorn with autodynatrace wrapper
  4. Start Gunicorn with a FastAPI application
  5. Send a request to Nginx
  6. Gunicorn will throw an Exception: [ERROR] Exception in ASGI application

Expected behavior According to the ASGI specifications for http connection scope, the information on "server" is optional (https://asgi.readthedocs.io/en/latest/specs/www.html). However the implementation of the middleware wrapper for FastAPI (wrappers/fastapi/middleware.py) assumes the [host, port] iterable unconditionally.

Since the specification allows a None value and two-item iterable [path, None] too, these two cases need to be handled as well.

Example fix:

        host = ""
        # https://asgi.readthedocs.io/en/latest/specs/www.html
        # Optional; if missing defaults to None.
        if scope.get("server"):
            if scope["server"][1]:
                # [host:port]
                host = "{}:{}".format(scope["server"][0], scope["server"][1])
            else:
                # [path, None]
                host = "{}".format(scope["server"][0])

Server:

theo-fokkinga commented 1 month ago

I've created a fix in PR #107