| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- import urllib
- from sentry_sdk.scope import should_send_default_pii
- from sentry_sdk.integrations._wsgi_common import _filter_headers
- from typing import TYPE_CHECKING
- if TYPE_CHECKING:
- from typing import Any
- from typing import Dict
- from typing import Optional
- from typing import Union
- from typing_extensions import Literal
- from sentry_sdk.utils import AnnotatedValue
- def _get_headers(asgi_scope: "Any") -> "Dict[str, str]":
- """
- Extract headers from the ASGI scope, in the format that the Sentry protocol expects.
- """
- headers: "Dict[str, str]" = {}
- for raw_key, raw_value in asgi_scope["headers"]:
- key = raw_key.decode("latin-1")
- value = raw_value.decode("latin-1")
- if key in headers:
- headers[key] = headers[key] + ", " + value
- else:
- headers[key] = value
- return headers
- def _get_url(
- asgi_scope: "Dict[str, Any]",
- default_scheme: "Literal['ws', 'http']",
- host: "Optional[Union[AnnotatedValue, str]]",
- ) -> str:
- """
- Extract URL from the ASGI scope, without also including the querystring.
- """
- scheme = asgi_scope.get("scheme", default_scheme)
- server = asgi_scope.get("server", None)
- path = asgi_scope.get("root_path", "") + asgi_scope.get("path", "")
- if host:
- return "%s://%s%s" % (scheme, host, path)
- if server is not None:
- host, port = server
- default_port = {"http": 80, "https": 443, "ws": 80, "wss": 443}.get(scheme)
- if port != default_port:
- return "%s://%s:%s%s" % (scheme, host, port, path)
- return "%s://%s%s" % (scheme, host, path)
- return path
- def _get_query(asgi_scope: "Any") -> "Any":
- """
- Extract querystring from the ASGI scope, in the format that the Sentry protocol expects.
- """
- qs = asgi_scope.get("query_string")
- if not qs:
- return None
- return urllib.parse.unquote(qs.decode("latin-1"))
- def _get_ip(asgi_scope: "Any") -> str:
- """
- Extract IP Address from the ASGI scope based on request headers with fallback to scope client.
- """
- headers = _get_headers(asgi_scope)
- try:
- return headers["x-forwarded-for"].split(",")[0].strip()
- except (KeyError, IndexError):
- pass
- try:
- return headers["x-real-ip"]
- except KeyError:
- pass
- return asgi_scope.get("client")[0]
- def _get_request_data(asgi_scope: "Any") -> "Dict[str, Any]":
- """
- Returns data related to the HTTP request from the ASGI scope.
- """
- request_data: "Dict[str, Any]" = {}
- ty = asgi_scope["type"]
- if ty in ("http", "websocket"):
- request_data["method"] = asgi_scope.get("method")
- request_data["headers"] = headers = _filter_headers(_get_headers(asgi_scope))
- request_data["query_string"] = _get_query(asgi_scope)
- request_data["url"] = _get_url(
- asgi_scope, "http" if ty == "http" else "ws", headers.get("host")
- )
- client = asgi_scope.get("client")
- if client and should_send_default_pii():
- request_data["env"] = {"REMOTE_ADDR": _get_ip(asgi_scope)}
- return request_data
- def _get_request_attributes(asgi_scope: "Any") -> "dict[str, Any]":
- """
- Return attributes related to the HTTP request from the ASGI scope.
- """
- attributes: "dict[str, Any]" = {}
- ty = asgi_scope["type"]
- if ty in ("http", "websocket"):
- if asgi_scope.get("method"):
- attributes["http.request.method"] = asgi_scope["method"].upper()
- headers = _filter_headers(_get_headers(asgi_scope), use_annotated_value=False)
- for header, value in headers.items():
- attributes[f"http.request.header.{header.lower()}"] = value
- query = _get_query(asgi_scope)
- if query:
- attributes["http.query"] = query
- attributes["url.full"] = _get_url(
- asgi_scope, "http" if ty == "http" else "ws", headers.get("host")
- )
- client = asgi_scope.get("client")
- if client and should_send_default_pii():
- attributes["client.address"] = _get_ip(asgi_scope)
- return attributes
|