We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Flask integration wrong User IP behind proxy
When sentry report bugs I get wrong users IP. 

I'm using UWSGI and NGINX stack. I forward X-REAL-IP from NGINX to UWSGI. Also flask.request.remote_addr shows correct user IP address but sentry-sdk version 0.7.10 shows my server IP address as user IP.

Would you please assist?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sentry_sdk/integrations/wsgi.py`
Content:
```
1 import sys
2 
3 from sentry_sdk.hub import Hub, _should_send_default_pii
4 from sentry_sdk.utils import capture_internal_exceptions, event_from_exception
5 from sentry_sdk._compat import PY2, reraise
6 from sentry_sdk.integrations._wsgi_common import _filter_headers
7 
8 if False:
9     from typing import Callable
10     from typing import Dict
11     from typing import List
12     from typing import Iterator
13     from typing import Any
14     from typing import Tuple
15     from typing import Optional
16 
17     from sentry_sdk.utils import ExcInfo
18 
19 
20 if PY2:
21 
22     def wsgi_decoding_dance(s, charset="utf-8", errors="replace"):
23         # type: (str, str, str) -> str
24         return s.decode(charset, errors)
25 
26 
27 else:
28 
29     def wsgi_decoding_dance(s, charset="utf-8", errors="replace"):
30         # type: (str, str, str) -> str
31         return s.encode("latin1").decode(charset, errors)
32 
33 
34 def get_host(environ):
35     # type: (Dict[str, str]) -> str
36     """Return the host for the given WSGI environment. Yanked from Werkzeug."""
37     if environ.get("HTTP_HOST"):
38         rv = environ["HTTP_HOST"]
39         if environ["wsgi.url_scheme"] == "http" and rv.endswith(":80"):
40             rv = rv[:-3]
41         elif environ["wsgi.url_scheme"] == "https" and rv.endswith(":443"):
42             rv = rv[:-4]
43     elif environ.get("SERVER_NAME"):
44         rv = environ["SERVER_NAME"]
45         if (environ["wsgi.url_scheme"], environ["SERVER_PORT"]) not in (
46             ("https", "443"),
47             ("http", "80"),
48         ):
49             rv += ":" + environ["SERVER_PORT"]
50     else:
51         # In spite of the WSGI spec, SERVER_NAME might not be present.
52         rv = "unknown"
53 
54     return rv
55 
56 
57 def get_request_url(environ):
58     # type: (Dict[str, str]) -> str
59     """Return the absolute URL without query string for the given WSGI
60     environment."""
61     return "%s://%s/%s" % (
62         environ.get("wsgi.url_scheme"),
63         get_host(environ),
64         wsgi_decoding_dance(environ.get("PATH_INFO") or "").lstrip("/"),
65     )
66 
67 
68 class SentryWsgiMiddleware(object):
69     __slots__ = ("app",)
70 
71     def __init__(self, app):
72         # type: (Callable) -> None
73         self.app = app
74 
75     def __call__(self, environ, start_response):
76         # type: (Dict[str, str], Callable) -> _ScopedResponse
77         hub = Hub(Hub.current)
78 
79         with hub:
80             with capture_internal_exceptions():
81                 with hub.configure_scope() as scope:
82                     scope.clear_breadcrumbs()
83                     scope._name = "wsgi"
84                     scope.add_event_processor(_make_wsgi_event_processor(environ))
85 
86             try:
87                 rv = self.app(environ, start_response)
88             except Exception:
89                 reraise(*_capture_exception(hub))
90 
91         return _ScopedResponse(hub, rv)
92 
93 
94 def _get_environ(environ):
95     # type: (Dict[str, str]) -> Iterator[Tuple[str, str]]
96     """
97     Returns our whitelisted environment variables.
98     """
99     keys = ("SERVER_NAME", "SERVER_PORT")
100     if _should_send_default_pii():
101         keys += ("REMOTE_ADDR",)  # type: ignore
102 
103     for key in keys:
104         if key in environ:
105             yield key, environ[key]
106 
107 
108 # `get_headers` comes from `werkzeug.datastructures.EnvironHeaders`
109 #
110 # We need this function because Django does not give us a "pure" http header
111 # dict. So we might as well use it for all WSGI integrations.
112 def _get_headers(environ):
113     # type: (Dict[str, str]) -> Iterator[Tuple[str, str]]
114     """
115     Returns only proper HTTP headers.
116 
117     """
118     for key, value in environ.items():
119         key = str(key)
120         if key.startswith("HTTP_") and key not in (
121             "HTTP_CONTENT_TYPE",
122             "HTTP_CONTENT_LENGTH",
123         ):
124             yield key[5:].replace("_", "-").title(), value
125         elif key in ("CONTENT_TYPE", "CONTENT_LENGTH"):
126             yield key.replace("_", "-").title(), value
127 
128 
129 def get_client_ip(environ):
130     # type: (Dict[str, str]) -> Optional[Any]
131     """
132     Naively yank the first IP address in an X-Forwarded-For header
133     and assume this is correct.
134 
135     Note: Don't use this in security sensitive situations since this
136     value may be forged from a client.
137     """
138     try:
139         return environ["HTTP_X_FORWARDED_FOR"].split(",")[0].strip()
140     except (KeyError, IndexError):
141         return environ.get("REMOTE_ADDR")
142 
143 
144 def _capture_exception(hub):
145     # type: (Hub) -> ExcInfo
146     # Check client here as it might have been unset while streaming response
147     if hub.client is not None:
148         exc_info = sys.exc_info()
149         event, hint = event_from_exception(
150             exc_info,
151             client_options=hub.client.options,
152             mechanism={"type": "wsgi", "handled": False},
153         )
154         hub.capture_event(event, hint=hint)
155     return exc_info
156 
157 
158 class _ScopedResponse(object):
159     __slots__ = ("_response", "_hub")
160 
161     def __init__(self, hub, response):
162         # type: (Hub, List[bytes]) -> None
163         self._hub = hub
164         self._response = response
165 
166     def __iter__(self):
167         # type: () -> Iterator[bytes]
168         iterator = iter(self._response)
169 
170         while True:
171             with self._hub:
172                 try:
173                     chunk = next(iterator)
174                 except StopIteration:
175                     break
176                 except Exception:
177                     reraise(*_capture_exception(self._hub))
178 
179             yield chunk
180 
181     def close(self):
182         with self._hub:
183             try:
184                 self._response.close()
185             except AttributeError:
186                 pass
187             except Exception:
188                 reraise(*_capture_exception(self._hub))
189 
190 
191 def _make_wsgi_event_processor(environ):
192     # type: (Dict[str, str]) -> Callable
193     # It's a bit unfortunate that we have to extract and parse the request data
194     # from the environ so eagerly, but there are a few good reasons for this.
195     #
196     # We might be in a situation where the scope/hub never gets torn down
197     # properly. In that case we will have an unnecessary strong reference to
198     # all objects in the environ (some of which may take a lot of memory) when
199     # we're really just interested in a few of them.
200     #
201     # Keeping the environment around for longer than the request lifecycle is
202     # also not necessarily something uWSGI can deal with:
203     # https://github.com/unbit/uwsgi/issues/1950
204 
205     client_ip = get_client_ip(environ)
206     request_url = get_request_url(environ)
207     query_string = environ.get("QUERY_STRING")
208     method = environ.get("REQUEST_METHOD")
209     env = dict(_get_environ(environ))
210     headers = _filter_headers(dict(_get_headers(environ)))
211 
212     def event_processor(event, hint):
213         # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
214         with capture_internal_exceptions():
215             # if the code below fails halfway through we at least have some data
216             request_info = event.setdefault("request", {})
217 
218             if _should_send_default_pii():
219                 user_info = event.setdefault("user", {})
220                 user_info["ip_address"] = client_ip
221 
222             request_info["url"] = request_url
223             request_info["query_string"] = query_string
224             request_info["method"] = method
225             request_info["env"] = env
226             request_info["headers"] = headers
227 
228         return event
229 
230     return event_processor
231
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/sentry_sdk/integrations/wsgi.py b/sentry_sdk/integrations/wsgi.py
--- a/sentry_sdk/integrations/wsgi.py
+++ b/sentry_sdk/integrations/wsgi.py
@@ -96,9 +96,10 @@
     """
     Returns our whitelisted environment variables.
     """
-    keys = ("SERVER_NAME", "SERVER_PORT")
+    keys = ["SERVER_NAME", "SERVER_PORT"]
     if _should_send_default_pii():
-        keys += ("REMOTE_ADDR",)  # type: ignore
+        # Add all three headers here to make debugging of proxy setup easier.
+        keys += ["REMOTE_ADDR", "HTTP_X_FORWARDED_FOR", "HTTP_X_REAL_IP"]
 
     for key in keys:
         if key in environ:
@@ -129,16 +130,21 @@
 def get_client_ip(environ):
     # type: (Dict[str, str]) -> Optional[Any]
     """
-    Naively yank the first IP address in an X-Forwarded-For header
-    and assume this is correct.
-
-    Note: Don't use this in security sensitive situations since this
-    value may be forged from a client.
+    Infer the user IP address from various headers. This cannot be used in
+    security sensitive situations since the value may be forged from a client,
+    but it's good enough for the event payload.
     """
     try:
         return environ["HTTP_X_FORWARDED_FOR"].split(",")[0].strip()
     except (KeyError, IndexError):
-        return environ.get("REMOTE_ADDR")
+        pass
+
+    try:
+        return environ["HTTP_X_REAL_IP"]
+    except KeyError:
+        pass
+
+    return environ.get("REMOTE_ADDR")
 
 
 def _capture_exception(hub):

{"golden_diff": "diff --git a/sentry_sdk/integrations/wsgi.py b/sentry_sdk/integrations/wsgi.py\n--- a/sentry_sdk/integrations/wsgi.py\n+++ b/sentry_sdk/integrations/wsgi.py\n@@ -96,9 +96,10 @@\n     \"\"\"\n     Returns our whitelisted environment variables.\n     \"\"\"\n-    keys = (\"SERVER_NAME\", \"SERVER_PORT\")\n+    keys = [\"SERVER_NAME\", \"SERVER_PORT\"]\n     if _should_send_default_pii():\n-        keys += (\"REMOTE_ADDR\",)  # type: ignore\n+        # Add all three headers here to make debugging of proxy setup easier.\n+        keys += [\"REMOTE_ADDR\", \"HTTP_X_FORWARDED_FOR\", \"HTTP_X_REAL_IP\"]\n \n     for key in keys:\n         if key in environ:\n@@ -129,16 +130,21 @@\n def get_client_ip(environ):\n     # type: (Dict[str, str]) -> Optional[Any]\n     \"\"\"\n-    Naively yank the first IP address in an X-Forwarded-For header\n-    and assume this is correct.\n-\n-    Note: Don't use this in security sensitive situations since this\n-    value may be forged from a client.\n+    Infer the user IP address from various headers. This cannot be used in\n+    security sensitive situations since the value may be forged from a client,\n+    but it's good enough for the event payload.\n     \"\"\"\n     try:\n         return environ[\"HTTP_X_FORWARDED_FOR\"].split(\",\")[0].strip()\n     except (KeyError, IndexError):\n-        return environ.get(\"REMOTE_ADDR\")\n+        pass\n+\n+    try:\n+        return environ[\"HTTP_X_REAL_IP\"]\n+    except KeyError:\n+        pass\n+\n+    return environ.get(\"REMOTE_ADDR\")\n \n \n def _capture_exception(hub):\n", "issue": "Flask integration wrong User IP behind proxy\nWhen sentry report bugs I get wrong users IP. \r\n\r\nI'm using UWSGI and NGINX stack. I forward X-REAL-IP from NGINX to UWSGI. Also flask.request.remote_addr shows correct user IP address but sentry-sdk version 0.7.10 shows my server IP address as user IP.\r\n\r\nWould you please assist?\n", "before_files": [{"content": "import sys\n\nfrom sentry_sdk.hub import Hub, _should_send_default_pii\nfrom sentry_sdk.utils import capture_internal_exceptions, event_from_exception\nfrom sentry_sdk._compat import PY2, reraise\nfrom sentry_sdk.integrations._wsgi_common import _filter_headers\n\nif False:\n    from typing import Callable\n    from typing import Dict\n    from typing import List\n    from typing import Iterator\n    from typing import Any\n    from typing import Tuple\n    from typing import Optional\n\n    from sentry_sdk.utils import ExcInfo\n\n\nif PY2:\n\n    def wsgi_decoding_dance(s, charset=\"utf-8\", errors=\"replace\"):\n        # type: (str, str, str) -> str\n        return s.decode(charset, errors)\n\n\nelse:\n\n    def wsgi_decoding_dance(s, charset=\"utf-8\", errors=\"replace\"):\n        # type: (str, str, str) -> str\n        return s.encode(\"latin1\").decode(charset, errors)\n\n\ndef get_host(environ):\n    # type: (Dict[str, str]) -> str\n    \"\"\"Return the host for the given WSGI environment. Yanked from Werkzeug.\"\"\"\n    if environ.get(\"HTTP_HOST\"):\n        rv = environ[\"HTTP_HOST\"]\n        if environ[\"wsgi.url_scheme\"] == \"http\" and rv.endswith(\":80\"):\n            rv = rv[:-3]\n        elif environ[\"wsgi.url_scheme\"] == \"https\" and rv.endswith(\":443\"):\n            rv = rv[:-4]\n    elif environ.get(\"SERVER_NAME\"):\n        rv = environ[\"SERVER_NAME\"]\n        if (environ[\"wsgi.url_scheme\"], environ[\"SERVER_PORT\"]) not in (\n            (\"https\", \"443\"),\n            (\"http\", \"80\"),\n        ):\n            rv += \":\" + environ[\"SERVER_PORT\"]\n    else:\n        # In spite of the WSGI spec, SERVER_NAME might not be present.\n        rv = \"unknown\"\n\n    return rv\n\n\ndef get_request_url(environ):\n    # type: (Dict[str, str]) -> str\n    \"\"\"Return the absolute URL without query string for the given WSGI\n    environment.\"\"\"\n    return \"%s://%s/%s\" % (\n        environ.get(\"wsgi.url_scheme\"),\n        get_host(environ),\n        wsgi_decoding_dance(environ.get(\"PATH_INFO\") or \"\").lstrip(\"/\"),\n    )\n\n\nclass SentryWsgiMiddleware(object):\n    __slots__ = (\"app\",)\n\n    def __init__(self, app):\n        # type: (Callable) -> None\n        self.app = app\n\n    def __call__(self, environ, start_response):\n        # type: (Dict[str, str], Callable) -> _ScopedResponse\n        hub = Hub(Hub.current)\n\n        with hub:\n            with capture_internal_exceptions():\n                with hub.configure_scope() as scope:\n                    scope.clear_breadcrumbs()\n                    scope._name = \"wsgi\"\n                    scope.add_event_processor(_make_wsgi_event_processor(environ))\n\n            try:\n                rv = self.app(environ, start_response)\n            except Exception:\n                reraise(*_capture_exception(hub))\n\n        return _ScopedResponse(hub, rv)\n\n\ndef _get_environ(environ):\n    # type: (Dict[str, str]) -> Iterator[Tuple[str, str]]\n    \"\"\"\n    Returns our whitelisted environment variables.\n    \"\"\"\n    keys = (\"SERVER_NAME\", \"SERVER_PORT\")\n    if _should_send_default_pii():\n        keys += (\"REMOTE_ADDR\",)  # type: ignore\n\n    for key in keys:\n        if key in environ:\n            yield key, environ[key]\n\n\n# `get_headers` comes from `werkzeug.datastructures.EnvironHeaders`\n#\n# We need this function because Django does not give us a \"pure\" http header\n# dict. So we might as well use it for all WSGI integrations.\ndef _get_headers(environ):\n    # type: (Dict[str, str]) -> Iterator[Tuple[str, str]]\n    \"\"\"\n    Returns only proper HTTP headers.\n\n    \"\"\"\n    for key, value in environ.items():\n        key = str(key)\n        if key.startswith(\"HTTP_\") and key not in (\n            \"HTTP_CONTENT_TYPE\",\n            \"HTTP_CONTENT_LENGTH\",\n        ):\n            yield key[5:].replace(\"_\", \"-\").title(), value\n        elif key in (\"CONTENT_TYPE\", \"CONTENT_LENGTH\"):\n            yield key.replace(\"_\", \"-\").title(), value\n\n\ndef get_client_ip(environ):\n    # type: (Dict[str, str]) -> Optional[Any]\n    \"\"\"\n    Naively yank the first IP address in an X-Forwarded-For header\n    and assume this is correct.\n\n    Note: Don't use this in security sensitive situations since this\n    value may be forged from a client.\n    \"\"\"\n    try:\n        return environ[\"HTTP_X_FORWARDED_FOR\"].split(\",\")[0].strip()\n    except (KeyError, IndexError):\n        return environ.get(\"REMOTE_ADDR\")\n\n\ndef _capture_exception(hub):\n    # type: (Hub) -> ExcInfo\n    # Check client here as it might have been unset while streaming response\n    if hub.client is not None:\n        exc_info = sys.exc_info()\n        event, hint = event_from_exception(\n            exc_info,\n            client_options=hub.client.options,\n            mechanism={\"type\": \"wsgi\", \"handled\": False},\n        )\n        hub.capture_event(event, hint=hint)\n    return exc_info\n\n\nclass _ScopedResponse(object):\n    __slots__ = (\"_response\", \"_hub\")\n\n    def __init__(self, hub, response):\n        # type: (Hub, List[bytes]) -> None\n        self._hub = hub\n        self._response = response\n\n    def __iter__(self):\n        # type: () -> Iterator[bytes]\n        iterator = iter(self._response)\n\n        while True:\n            with self._hub:\n                try:\n                    chunk = next(iterator)\n                except StopIteration:\n                    break\n                except Exception:\n                    reraise(*_capture_exception(self._hub))\n\n            yield chunk\n\n    def close(self):\n        with self._hub:\n            try:\n                self._response.close()\n            except AttributeError:\n                pass\n            except Exception:\n                reraise(*_capture_exception(self._hub))\n\n\ndef _make_wsgi_event_processor(environ):\n    # type: (Dict[str, str]) -> Callable\n    # It's a bit unfortunate that we have to extract and parse the request data\n    # from the environ so eagerly, but there are a few good reasons for this.\n    #\n    # We might be in a situation where the scope/hub never gets torn down\n    # properly. In that case we will have an unnecessary strong reference to\n    # all objects in the environ (some of which may take a lot of memory) when\n    # we're really just interested in a few of them.\n    #\n    # Keeping the environment around for longer than the request lifecycle is\n    # also not necessarily something uWSGI can deal with:\n    # https://github.com/unbit/uwsgi/issues/1950\n\n    client_ip = get_client_ip(environ)\n    request_url = get_request_url(environ)\n    query_string = environ.get(\"QUERY_STRING\")\n    method = environ.get(\"REQUEST_METHOD\")\n    env = dict(_get_environ(environ))\n    headers = _filter_headers(dict(_get_headers(environ)))\n\n    def event_processor(event, hint):\n        # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]\n        with capture_internal_exceptions():\n            # if the code below fails halfway through we at least have some data\n            request_info = event.setdefault(\"request\", {})\n\n            if _should_send_default_pii():\n                user_info = event.setdefault(\"user\", {})\n                user_info[\"ip_address\"] = client_ip\n\n            request_info[\"url\"] = request_url\n            request_info[\"query_string\"] = query_string\n            request_info[\"method\"] = method\n            request_info[\"env\"] = env\n            request_info[\"headers\"] = headers\n\n        return event\n\n    return event_processor\n", "path": "sentry_sdk/integrations/wsgi.py"}], "after_files": [{"content": "import sys\n\nfrom sentry_sdk.hub import Hub, _should_send_default_pii\nfrom sentry_sdk.utils import capture_internal_exceptions, event_from_exception\nfrom sentry_sdk._compat import PY2, reraise\nfrom sentry_sdk.integrations._wsgi_common import _filter_headers\n\nif False:\n    from typing import Callable\n    from typing import Dict\n    from typing import List\n    from typing import Iterator\n    from typing import Any\n    from typing import Tuple\n    from typing import Optional\n\n    from sentry_sdk.utils import ExcInfo\n\n\nif PY2:\n\n    def wsgi_decoding_dance(s, charset=\"utf-8\", errors=\"replace\"):\n        # type: (str, str, str) -> str\n        return s.decode(charset, errors)\n\n\nelse:\n\n    def wsgi_decoding_dance(s, charset=\"utf-8\", errors=\"replace\"):\n        # type: (str, str, str) -> str\n        return s.encode(\"latin1\").decode(charset, errors)\n\n\ndef get_host(environ):\n    # type: (Dict[str, str]) -> str\n    \"\"\"Return the host for the given WSGI environment. Yanked from Werkzeug.\"\"\"\n    if environ.get(\"HTTP_HOST\"):\n        rv = environ[\"HTTP_HOST\"]\n        if environ[\"wsgi.url_scheme\"] == \"http\" and rv.endswith(\":80\"):\n            rv = rv[:-3]\n        elif environ[\"wsgi.url_scheme\"] == \"https\" and rv.endswith(\":443\"):\n            rv = rv[:-4]\n    elif environ.get(\"SERVER_NAME\"):\n        rv = environ[\"SERVER_NAME\"]\n        if (environ[\"wsgi.url_scheme\"], environ[\"SERVER_PORT\"]) not in (\n            (\"https\", \"443\"),\n            (\"http\", \"80\"),\n        ):\n            rv += \":\" + environ[\"SERVER_PORT\"]\n    else:\n        # In spite of the WSGI spec, SERVER_NAME might not be present.\n        rv = \"unknown\"\n\n    return rv\n\n\ndef get_request_url(environ):\n    # type: (Dict[str, str]) -> str\n    \"\"\"Return the absolute URL without query string for the given WSGI\n    environment.\"\"\"\n    return \"%s://%s/%s\" % (\n        environ.get(\"wsgi.url_scheme\"),\n        get_host(environ),\n        wsgi_decoding_dance(environ.get(\"PATH_INFO\") or \"\").lstrip(\"/\"),\n    )\n\n\nclass SentryWsgiMiddleware(object):\n    __slots__ = (\"app\",)\n\n    def __init__(self, app):\n        # type: (Callable) -> None\n        self.app = app\n\n    def __call__(self, environ, start_response):\n        # type: (Dict[str, str], Callable) -> _ScopedResponse\n        hub = Hub(Hub.current)\n\n        with hub:\n            with capture_internal_exceptions():\n                with hub.configure_scope() as scope:\n                    scope.clear_breadcrumbs()\n                    scope._name = \"wsgi\"\n                    scope.add_event_processor(_make_wsgi_event_processor(environ))\n\n            try:\n                rv = self.app(environ, start_response)\n            except Exception:\n                reraise(*_capture_exception(hub))\n\n        return _ScopedResponse(hub, rv)\n\n\ndef _get_environ(environ):\n    # type: (Dict[str, str]) -> Iterator[Tuple[str, str]]\n    \"\"\"\n    Returns our whitelisted environment variables.\n    \"\"\"\n    keys = [\"SERVER_NAME\", \"SERVER_PORT\"]\n    if _should_send_default_pii():\n        # Add all three headers here to make debugging of proxy setup easier.\n        keys += [\"REMOTE_ADDR\", \"HTTP_X_FORWARDED_FOR\", \"HTTP_X_REAL_IP\"]\n\n    for key in keys:\n        if key in environ:\n            yield key, environ[key]\n\n\n# `get_headers` comes from `werkzeug.datastructures.EnvironHeaders`\n#\n# We need this function because Django does not give us a \"pure\" http header\n# dict. So we might as well use it for all WSGI integrations.\ndef _get_headers(environ):\n    # type: (Dict[str, str]) -> Iterator[Tuple[str, str]]\n    \"\"\"\n    Returns only proper HTTP headers.\n\n    \"\"\"\n    for key, value in environ.items():\n        key = str(key)\n        if key.startswith(\"HTTP_\") and key not in (\n            \"HTTP_CONTENT_TYPE\",\n            \"HTTP_CONTENT_LENGTH\",\n        ):\n            yield key[5:].replace(\"_\", \"-\").title(), value\n        elif key in (\"CONTENT_TYPE\", \"CONTENT_LENGTH\"):\n            yield key.replace(\"_\", \"-\").title(), value\n\n\ndef get_client_ip(environ):\n    # type: (Dict[str, str]) -> Optional[Any]\n    \"\"\"\n    Infer the user IP address from various headers. This cannot be used in\n    security sensitive situations since the value may be forged from a client,\n    but it's good enough for the event payload.\n    \"\"\"\n    try:\n        return environ[\"HTTP_X_FORWARDED_FOR\"].split(\",\")[0].strip()\n    except (KeyError, IndexError):\n        pass\n\n    try:\n        return environ[\"HTTP_X_REAL_IP\"]\n    except KeyError:\n        pass\n\n    return environ.get(\"REMOTE_ADDR\")\n\n\ndef _capture_exception(hub):\n    # type: (Hub) -> ExcInfo\n    # Check client here as it might have been unset while streaming response\n    if hub.client is not None:\n        exc_info = sys.exc_info()\n        event, hint = event_from_exception(\n            exc_info,\n            client_options=hub.client.options,\n            mechanism={\"type\": \"wsgi\", \"handled\": False},\n        )\n        hub.capture_event(event, hint=hint)\n    return exc_info\n\n\nclass _ScopedResponse(object):\n    __slots__ = (\"_response\", \"_hub\")\n\n    def __init__(self, hub, response):\n        # type: (Hub, List[bytes]) -> None\n        self._hub = hub\n        self._response = response\n\n    def __iter__(self):\n        # type: () -> Iterator[bytes]\n        iterator = iter(self._response)\n\n        while True:\n            with self._hub:\n                try:\n                    chunk = next(iterator)\n                except StopIteration:\n                    break\n                except Exception:\n                    reraise(*_capture_exception(self._hub))\n\n            yield chunk\n\n    def close(self):\n        with self._hub:\n            try:\n                self._response.close()\n            except AttributeError:\n                pass\n            except Exception:\n                reraise(*_capture_exception(self._hub))\n\n\ndef _make_wsgi_event_processor(environ):\n    # type: (Dict[str, str]) -> Callable\n    # It's a bit unfortunate that we have to extract and parse the request data\n    # from the environ so eagerly, but there are a few good reasons for this.\n    #\n    # We might be in a situation where the scope/hub never gets torn down\n    # properly. In that case we will have an unnecessary strong reference to\n    # all objects in the environ (some of which may take a lot of memory) when\n    # we're really just interested in a few of them.\n    #\n    # Keeping the environment around for longer than the request lifecycle is\n    # also not necessarily something uWSGI can deal with:\n    # https://github.com/unbit/uwsgi/issues/1950\n\n    client_ip = get_client_ip(environ)\n    request_url = get_request_url(environ)\n    query_string = environ.get(\"QUERY_STRING\")\n    method = environ.get(\"REQUEST_METHOD\")\n    env = dict(_get_environ(environ))\n    headers = _filter_headers(dict(_get_headers(environ)))\n\n    def event_processor(event, hint):\n        # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]\n        with capture_internal_exceptions():\n            # if the code below fails halfway through we at least have some data\n            request_info = event.setdefault(\"request\", {})\n\n            if _should_send_default_pii():\n                user_info = event.setdefault(\"user\", {})\n                user_info[\"ip_address\"] = client_ip\n\n            request_info[\"url\"] = request_url\n            request_info[\"query_string\"] = query_string\n            request_info[\"method\"] = method\n            request_info[\"env\"] = env\n            request_info[\"headers\"] = headers\n\n        return event\n\n    return event_processor\n", "path": "sentry_sdk/integrations/wsgi.py"}]}