Skip to content

Commit 8969b51

Browse files
fix(requests): ensure http metadata (#2592)
Call `set_http_meta` once and with all the available data. Also avoid urlparse instead of simpler parsing for better performance. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 508f991 commit 8969b51

File tree

2 files changed

+89
-17
lines changed

2 files changed

+89
-17
lines changed

ddtrace/contrib/requests/connection.py

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,57 @@
1+
from typing import Optional
2+
13
import ddtrace
24
from ddtrace import config
35

46
from .. import trace_utils
57
from ...constants import ANALYTICS_SAMPLE_RATE_KEY
68
from ...constants import SPAN_MEASURED_KEY
79
from ...ext import SpanTypes
8-
from ...internal.compat import parse
910
from ...internal.logger import get_logger
1011
from ...propagation.http import HTTPPropagator
1112

1213

1314
log = get_logger(__name__)
1415

1516

17+
def _extract_hostname(uri):
18+
# type: (str) -> str
19+
end = len(uri)
20+
j = uri.rfind("#", 0, end)
21+
if j != -1:
22+
end = j
23+
j = uri.rfind("&", 0, end)
24+
if j != -1:
25+
end = j
26+
27+
start = uri.find("://", 0, end) + 3
28+
i = uri.find("@", start, end) + 1
29+
if i != 0:
30+
start = i
31+
j = uri.find("/", start, end)
32+
if j != -1:
33+
end = j
34+
35+
return uri[start:end]
36+
37+
38+
def _extract_query_string(uri):
39+
# type: (str) -> Optional[str]
40+
start = uri.find("?") + 1
41+
if start == 0:
42+
return None
43+
44+
end = len(uri)
45+
j = uri.rfind("#", 0, end)
46+
if j != -1:
47+
end = j
48+
49+
if end <= start:
50+
return None
51+
52+
return uri[start:end]
53+
54+
1655
def _wrap_send(func, instance, args, kwargs):
1756
"""Trace the `Session.send` instance method"""
1857
# TODO[manu]: we already offer a way to provide the Global Tracer
@@ -29,10 +68,8 @@ def _wrap_send(func, instance, args, kwargs):
2968
if not request:
3069
return func(*args, **kwargs)
3170

32-
parsed_uri = parse.urlparse(request.url)
33-
hostname = parsed_uri.hostname
34-
if parsed_uri.port:
35-
hostname = "{}:{}".format(hostname, parsed_uri.port)
71+
url = request.url
72+
hostname = _extract_hostname(url)
3673

3774
cfg = config.get_from(instance)
3875
service = None
@@ -59,19 +96,9 @@ def _wrap_send(func, instance, args, kwargs):
5996
if cfg.get("distributed_tracing"):
6097
HTTPPropagator.inject(span.context, request.headers)
6198

62-
response = None
99+
response = response_headers = None
63100
try:
64101
response = func(*args, **kwargs)
65-
66-
# Storing response headers in the span. Note that response.headers is not a dict, but an iterable
67-
# requests custom structure, that we convert to a dict
68-
if hasattr(response, "headers"):
69-
response_headers = response.headers
70-
else:
71-
response_headers = None
72-
trace_utils.set_http_meta(
73-
span, config.requests, request_headers=request.headers, response_headers=response_headers
74-
)
75102
return response
76103
finally:
77104
try:
@@ -82,13 +109,16 @@ def _wrap_send(func, instance, args, kwargs):
82109
# Note that response.headers is not a dict, but an iterable
83110
# requests custom structure, that we convert to a dict
84111
response_headers = dict(getattr(response, "headers", {}))
112+
85113
trace_utils.set_http_meta(
86114
span,
87115
config.requests,
116+
request_headers=request.headers,
117+
response_headers=response_headers,
88118
method=request.method.upper(),
89119
url=request.url,
90120
status_code=status,
91-
query=parsed_uri.query,
121+
query=_extract_query_string(url),
92122
)
93123
except Exception:
94124
log.debug("requests: error adding tags", exc_info=True)

tests/contrib/requests/test_requests.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from ddtrace.constants import ANALYTICS_SAMPLE_RATE_KEY
1313
from ddtrace.contrib.requests import patch
1414
from ddtrace.contrib.requests import unpatch
15+
from ddtrace.contrib.requests.connection import _extract_hostname
16+
from ddtrace.contrib.requests.connection import _extract_query_string
1517
from ddtrace.ext import errors
1618
from ddtrace.ext import http
1719
from tests.opentracer.utils import init_tracer
@@ -567,3 +569,43 @@ def test_traced_session_no_patch_all(tmpdir):
567569
assert p.stderr.read() == six.b("")
568570
assert p.stdout.read() == six.b("")
569571
assert p.returncode == 0
572+
573+
574+
@pytest.mark.parametrize(
575+
"uri,hostname",
576+
[
577+
("http://localhost:8080", "localhost:8080"),
578+
("http://localhost:8080/", "localhost:8080"),
579+
("http://localhost", "localhost"),
580+
("http://localhost/", "localhost"),
581+
("http://asd:wwefwf@localhost:8080", "localhost:8080"),
582+
("http://asd:wwefwf@localhost:8080/", "localhost:8080"),
583+
("http://asd:wwefwf@localhost:8080/path", "localhost:8080"),
584+
("http://asd:wwefwf@localhost:8080/path?query", "localhost:8080"),
585+
("http://asd:wwefwf@localhost:8080/path?query#fragment", "localhost:8080"),
586+
("http://asd:wwefwf@localhost:8080/path#frag?ment", "localhost:8080"),
587+
("http://localhost:8080/path#frag?ment", "localhost:8080"),
588+
("http://localhost/path#frag?ment", "localhost"),
589+
],
590+
)
591+
def test_extract_hostname(uri, hostname):
592+
assert _extract_hostname(uri) == hostname
593+
594+
595+
@pytest.mark.parametrize(
596+
"uri,qs",
597+
[
598+
("http://localhost:8080", None),
599+
("http://localhost", None),
600+
("http://asd:wwefwf@localhost:8080", None),
601+
("http://asd:wwefwf@localhost:8080/path", None),
602+
("http://asd:wwefwf@localhost:8080/path?query", "query"),
603+
("http://asd:wwefwf@localhost:8080/path?query#fragment", "query"),
604+
("http://asd:wwefwf@localhost:8080/path#frag?ment", None),
605+
("http://localhost:8080/path#frag?ment", None),
606+
("http://localhost/path#frag?ment", None),
607+
("http://localhost?query", "query"),
608+
],
609+
)
610+
def test_extract_query_string(uri, qs):
611+
assert _extract_query_string(uri) == qs

0 commit comments

Comments
 (0)