Skip to content

Commit af0732c

Browse files
committed
feat(tornado): Add sentry.is_localhost for inbound filters
1 parent 4e6beb8 commit af0732c

2 files changed

Lines changed: 65 additions & 0 deletions

File tree

‎sentry_sdk/integrations/tornado.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
CONTEXTVARS_ERROR_MESSAGE,
2323
HAS_REAL_CONTEXTVARS,
2424
AnnotatedValue,
25+
_is_localhost,
2526
capture_internal_exceptions,
2627
ensure_integration_enabled,
2728
event_from_exception,
@@ -127,6 +128,15 @@ def _handle_request_impl(self: "RequestHandler") -> "Generator[None, None, None]
127128
processor = _make_event_processor(weak_handler)
128129
scope.add_event_processor(processor)
129130

131+
scope.set_attribute(
132+
SPANDATA.SENTRY_IS_LOCALHOST,
133+
_is_localhost(
134+
client_ip=_get_client_ip(self.request),
135+
host_header=self.request.headers.get("Host"),
136+
forwarded_host_header=self.request.headers.get("X-Forwarded-Host"),
137+
),
138+
)
139+
130140
span_ctx: "ContextManager[Union[Span, StreamedSpan, None]]"
131141

132142
if is_span_streaming_enabled:
@@ -198,6 +208,13 @@ def _handle_request_impl(self: "RequestHandler") -> "Generator[None, None, None]
198208
span.status = "error" if status_int >= 400 else "ok"
199209

200210

211+
def _get_client_ip(request: "Any") -> "Optional[str]":
212+
xff = request.headers.get("X-Forwarded-For", "").split(",")[0].strip()
213+
if xff:
214+
return xff
215+
return request.headers.get("X-Real-IP") or request.remote_ip
216+
217+
201218
def _get_request_attributes(request: "Any") -> "Dict[str, Any]":
202219
attributes = {} # type: Dict[str, Any]
203220
client_options = sentry_sdk.get_client().options

‎tests/integrations/tornado/test_tornado.py‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,54 @@ def test_client_address_span_attribute_data_collection(
11181118
assert "client.address" not in server_span["attributes"]
11191119

11201120

1121+
@pytest.mark.parametrize(
1122+
"forwarded_for, host_header, is_localhost",
1123+
[
1124+
pytest.param(None, None, True, id="loopback-ip"),
1125+
pytest.param(
1126+
"93.184.216.34", "localhost:8080", True, id="localhost-host-header"
1127+
),
1128+
pytest.param("93.184.216.34", "example.com", False, id="non-local"),
1129+
],
1130+
)
1131+
def test_is_localhost_span_attribute(
1132+
tornado_testcase,
1133+
sentry_init,
1134+
capture_items,
1135+
forwarded_for,
1136+
host_header,
1137+
is_localhost,
1138+
):
1139+
from sentry_sdk.consts import SPANDATA
1140+
1141+
sentry_init(
1142+
integrations=[TornadoIntegration()],
1143+
traces_sample_rate=1.0,
1144+
trace_lifecycle="stream",
1145+
)
1146+
1147+
items = capture_items("span")
1148+
1149+
client = tornado_testcase(Application([(r"/hi", ChildSpanHandler)]))
1150+
headers = {}
1151+
if forwarded_for:
1152+
headers["X-Forwarded-For"] = forwarded_for
1153+
if host_header:
1154+
headers["Host"] = host_header
1155+
client.fetch("/hi", headers=headers if headers else None)
1156+
1157+
sentry_sdk.flush()
1158+
1159+
child_spans = [item.payload for item in items if not item.payload.get("is_segment")]
1160+
server_spans = [item.payload for item in items if item.payload.get("is_segment")]
1161+
1162+
assert len(server_spans) == 1
1163+
assert len(child_spans) == 1
1164+
1165+
assert server_spans[0]["attributes"][SPANDATA.SENTRY_IS_LOCALHOST] == is_localhost
1166+
assert child_spans[0]["attributes"][SPANDATA.SENTRY_IS_LOCALHOST] == is_localhost
1167+
1168+
11211169
@pytest.mark.parametrize(
11221170
"init_kwargs, expect_remote_addr", DATA_COLLECTION_REMOTE_ADDR_CASES
11231171
)

0 commit comments

Comments
 (0)