11import urllib
2+ from enum import Enum
23from typing import TYPE_CHECKING
34
45from sentry_sdk .integrations ._wsgi_common import _filter_headers
1213 from sentry_sdk .utils import AnnotatedValue
1314
1415
16+ class _RootPathInPath (Enum ):
17+ EXCLUDED = "excluded"
18+ EITHER = "either"
19+
20+
1521def _get_headers (asgi_scope : "Any" ) -> "Dict[str, str]" :
1622 """
1723 Extract headers from the ASGI scope, in the format that the Sentry protocol expects.
@@ -28,18 +34,34 @@ def _get_headers(asgi_scope: "Any") -> "Dict[str, str]":
2834 return headers
2935
3036
37+ def _get_path (
38+ asgi_scope : "Dict[str, Any]" , root_path_in_path : "_RootPathInPath"
39+ ) -> "str" :
40+ if root_path_in_path is _RootPathInPath .EXCLUDED :
41+ return asgi_scope .get ("root_path" , "" ) + asgi_scope .get ("path" , "" )
42+
43+ # Inverse of https://github.com/Kludex/starlette/blob/de970d7b3facb853eb7ad077decbf3d94f2aab6c/starlette/_utils.py#L96
44+ path = asgi_scope ["path" ]
45+ root_path = asgi_scope .get ("root_path" , "" )
46+
47+ if not root_path or path == root_path or path .startswith (root_path + "/" ):
48+ return path
49+
50+ return root_path + path
51+
52+
3153def _get_url (
3254 asgi_scope : "Dict[str, Any]" ,
3355 default_scheme : "Literal['ws', 'http']" ,
3456 host : "Optional[Union[AnnotatedValue, str]]" ,
57+ path : str ,
3558) -> str :
3659 """
3760 Extract URL from the ASGI scope, without also including the querystring.
3861 """
3962 scheme = asgi_scope .get ("scheme" , default_scheme )
4063
4164 server = asgi_scope .get ("server" , None )
42- path = asgi_scope .get ("root_path" , "" ) + asgi_scope .get ("path" , "" )
4365
4466 if host :
4567 return "%s://%s%s" % (scheme , host , path )
@@ -81,7 +103,10 @@ def _get_ip(asgi_scope: "Any") -> str:
81103 return asgi_scope .get ("client" )[0 ]
82104
83105
84- def _get_request_data (asgi_scope : "Any" ) -> "Dict[str, Any]" :
106+ def _get_request_data (
107+ asgi_scope : "Any" ,
108+ root_path_in_path : "_RootPathInPath" ,
109+ ) -> "Dict[str, Any]" :
85110 """
86111 Returns data related to the HTTP request from the ASGI scope.
87112 """
@@ -96,7 +121,10 @@ def _get_request_data(asgi_scope: "Any") -> "Dict[str, Any]":
96121 request_data ["query_string" ] = _get_query (asgi_scope )
97122
98123 request_data ["url" ] = _get_url (
99- asgi_scope , "http" if ty == "http" else "ws" , headers .get ("host" )
124+ asgi_scope ,
125+ "http" if ty == "http" else "ws" ,
126+ headers .get ("host" ),
127+ path = _get_path (asgi_scope = asgi_scope , root_path_in_path = root_path_in_path ),
100128 )
101129
102130 client = asgi_scope .get ("client" )
@@ -106,7 +134,10 @@ def _get_request_data(asgi_scope: "Any") -> "Dict[str, Any]":
106134 return request_data
107135
108136
109- def _get_request_attributes (asgi_scope : "Any" ) -> "dict[str, Any]" :
137+ def _get_request_attributes (
138+ asgi_scope : "Any" ,
139+ root_path_in_path : "_RootPathInPath" ,
140+ ) -> "dict[str, Any]" :
110141 """
111142 Return attributes related to the HTTP request from the ASGI scope.
112143 """
@@ -126,18 +157,21 @@ def _get_request_attributes(asgi_scope: "Any") -> "dict[str, Any]":
126157 if query :
127158 attributes ["http.query" ] = query
128159
160+ path = _get_path (asgi_scope = asgi_scope , root_path_in_path = root_path_in_path )
161+ attributes ["url.path" ] = path
162+
129163 url_without_query_string = _get_url (
130- asgi_scope , "http" if ty == "http" else "ws" , headers .get ("host" )
164+ asgi_scope ,
165+ "http" if ty == "http" else "ws" ,
166+ headers .get ("host" ),
167+ path = path ,
131168 )
132169 query_string = _get_query (asgi_scope )
133170 attributes ["url.full" ] = (
134171 f"{ url_without_query_string } ?{ query_string } "
135172 if query_string is not None
136173 else url_without_query_string
137174 )
138- attributes ["url.path" ] = asgi_scope .get ("root_path" , "" ) + asgi_scope .get (
139- "path" , ""
140- )
141175
142176 client = asgi_scope .get ("client" )
143177 if client and should_send_default_pii ():
0 commit comments