Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions sentry_sdk/integrations/stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,17 @@ def _install_httplib() -> None:
def putrequest(
self: "HTTPConnection", method: str, url: str, *args: "Any", **kwargs: "Any"
) -> "Any":
host = self.host
port = self.port
default_port = self.default_port

# proxies go through set_tunnel
tunnel_host = getattr(self, "_tunnel_host", None)
if tunnel_host:
host = tunnel_host
port = getattr(self, "_tunnel_port", default_port)
else:
host = self.host
port = self.port

client = sentry_sdk.get_client()
if client.get_integration(StdlibIntegration) is None or is_sentry_url(
client, host
Expand Down Expand Up @@ -104,6 +111,11 @@ def putrequest(
span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)

# for proxies, these point to the proxy host/port
if tunnel_host:
span.set_data(SPANDATA.NETWORK_PEER_ADDRESS, self.host)
span.set_data(SPANDATA.NETWORK_PEER_PORT, self.port)

rv = real_putrequest(self, method, url, *args, **kwargs)

if should_propagate_trace(client, real_url):
Expand Down
51 changes: 50 additions & 1 deletion tests/integrations/stdlib/test_httplib.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import os
import datetime
import socket
from http.client import HTTPConnection, HTTPSConnection
from http.server import BaseHTTPRequestHandler, HTTPServer
from socket import SocketIO
from threading import Thread
from urllib.error import HTTPError
from urllib.request import urlopen
from unittest import mock
Expand All @@ -12,11 +15,35 @@
from sentry_sdk.consts import MATCH_ALL, SPANDATA
from sentry_sdk.integrations.stdlib import StdlibIntegration

from tests.conftest import ApproxDict, create_mock_http_server
from tests.conftest import ApproxDict, create_mock_http_server, get_free_port

PORT = create_mock_http_server()


class MockProxyRequestHandler(BaseHTTPRequestHandler):
def do_CONNECT(self):
self.send_response(200, "Connection Established")
self.end_headers()

self.rfile.readline()

response = b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
self.wfile.write(response)
self.wfile.flush()


def create_mock_proxy_server():
proxy_port = get_free_port()
proxy_server = HTTPServer(("localhost", proxy_port), MockProxyRequestHandler)
proxy_thread = Thread(target=proxy_server.serve_forever)
proxy_thread.daemon = True
proxy_thread.start()
return proxy_port


PROXY_PORT = create_mock_proxy_server()


def test_crumb_capture(sentry_init, capture_events):
sentry_init(integrations=[StdlibIntegration()])
events = capture_events()
Expand Down Expand Up @@ -642,3 +669,25 @@ def test_http_timeout(monkeypatch, sentry_init, capture_envelopes):
span = transaction["spans"][0]
assert span["op"] == "http.client"
assert span["description"] == f"GET http://localhost:{PORT}/bla" # noqa: E231


@pytest.mark.parametrize("tunnel_port", [8080, None])
def test_proxy_http_tunnel(sentry_init, capture_events, tunnel_port):
sentry_init(traces_sample_rate=1.0)
events = capture_events()

with start_transaction(name="test_transaction"):
conn = HTTPConnection("localhost", PROXY_PORT)
conn.set_tunnel("api.example.com", tunnel_port)
conn.request("GET", "/foo")
conn.getresponse()

(event,) = events
(span,) = event["spans"]

port_modifier = f":{tunnel_port}" if tunnel_port else ""
assert span["description"] == f"GET http://api.example.com{port_modifier}/foo"
assert span["data"]["url"] == f"http://api.example.com{port_modifier}/foo"
assert span["data"][SPANDATA.HTTP_METHOD] == "GET"
assert span["data"][SPANDATA.NETWORK_PEER_ADDRESS] == "localhost"
assert span["data"][SPANDATA.NETWORK_PEER_PORT] == PROXY_PORT
Loading