Skip to content

Commit df23246

Browse files
patch send instead of request, captures both stream and request
1 parent 35d04cc commit df23246

4 files changed

Lines changed: 958 additions & 48 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,6 @@ __marimo__/
222222

223223
# macOS
224224
.DS_Store
225+
226+
# Bug tracking
227+
**/BUG_TRACKING.md

drift/instrumentation/httpx/e2e-tests/src/app.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,106 @@ async def fetch():
361361
return jsonify({"error": str(e)}), 500
362362

363363

364+
# =============================================================================
365+
# BUG HUNTING TESTS - Confirmed Instrumentation Bugs
366+
# These endpoints expose bugs in the httpx instrumentation
367+
# =============================================================================
368+
369+
370+
@app.route("/test/streaming", methods=["GET"])
371+
def test_streaming():
372+
"""Test 5: Streaming response using client.stream() context manager."""
373+
try:
374+
with httpx.Client() as client:
375+
with client.stream("GET", "https://jsonplaceholder.typicode.com/posts/6") as response:
376+
# Read the streaming response
377+
content = response.read()
378+
data = response.json()
379+
return jsonify(data)
380+
except Exception as e:
381+
return jsonify({"error": str(e)}), 500
382+
383+
384+
@app.route("/test/toplevel-stream", methods=["GET"])
385+
def test_toplevel_stream():
386+
"""Test 6: Top-level httpx.stream() context manager."""
387+
try:
388+
with httpx.stream("GET", "https://jsonplaceholder.typicode.com/posts/7") as response:
389+
content = response.read()
390+
data = response.json()
391+
return jsonify(data)
392+
except Exception as e:
393+
return jsonify({"error": str(e)}), 500
394+
395+
396+
@app.route("/test/multipart-files", methods=["POST"])
397+
def test_multipart_files():
398+
"""Test 7: Multipart file upload using files= parameter."""
399+
try:
400+
# Create in-memory file-like content
401+
files = {"file": ("test.txt", b"Hello, World!", "text/plain")}
402+
with httpx.Client() as client:
403+
# Use httpbin.org which echoes back file uploads
404+
response = client.post(
405+
"https://httpbin.org/post",
406+
files=files,
407+
)
408+
result = response.json()
409+
return jsonify({"uploaded": True, "files": result.get("files", {})})
410+
except Exception as e:
411+
return jsonify({"error": str(e)}), 500
412+
413+
414+
@app.route("/test/follow-redirects", methods=["GET"])
415+
def test_follow_redirects():
416+
"""Test 12: Following HTTP redirects."""
417+
try:
418+
with httpx.Client(follow_redirects=True) as client:
419+
# httpbin.org/redirect/2 will redirect twice before returning
420+
response = client.get("https://httpbin.org/redirect/2")
421+
return jsonify({
422+
"final_url": str(response.url),
423+
"status_code": response.status_code,
424+
"redirect_count": len(response.history),
425+
})
426+
except Exception as e:
427+
return jsonify({"error": str(e)}), 500
428+
429+
430+
@app.route("/test/async-send", methods=["GET"])
431+
def test_async_send():
432+
"""Test 14: AsyncClient.send() method - bypasses AsyncClient.request()."""
433+
434+
async def fetch():
435+
async with httpx.AsyncClient() as client:
436+
req = client.build_request("GET", "https://jsonplaceholder.typicode.com/posts/10")
437+
response = await client.send(req)
438+
return response.json()
439+
440+
try:
441+
result = asyncio.run(fetch())
442+
return jsonify(result)
443+
except Exception as e:
444+
return jsonify({"error": str(e)}), 500
445+
446+
447+
@app.route("/test/async-stream", methods=["GET"])
448+
def test_async_stream():
449+
"""Test 15: Async streaming response using AsyncClient.stream()."""
450+
451+
async def fetch():
452+
async with httpx.AsyncClient() as client:
453+
async with client.stream("GET", "https://jsonplaceholder.typicode.com/posts/11") as response:
454+
await response.aread()
455+
return response.json()
456+
457+
try:
458+
result = asyncio.run(fetch())
459+
return jsonify(result)
460+
except Exception as e:
461+
return jsonify({"error": str(e)}), 500
462+
463+
364464
if __name__ == "__main__":
365465
sdk.mark_app_as_ready()
366466
app.run(host="0.0.0.0", port=8000, debug=False)

drift/instrumentation/httpx/e2e-tests/src/test_requests.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,28 @@ def make_request(method, endpoint, **kwargs):
100100
# Async sequential chained requests
101101
make_request("GET", "/api/async/chain")
102102

103+
# ==========================================================================
104+
# Bug Hunting Tests - Confirmed Instrumentation Bugs
105+
# These tests expose bugs in the httpx instrumentation
106+
# ==========================================================================
107+
print("\n--- Bug Hunting Tests (Confirmed Bugs) ---\n")
108+
109+
# BUG: Streaming response - triggers unpatched socket calls during replay
110+
make_request("GET", "/test/streaming")
111+
112+
# BUG: Top-level stream - triggers unpatched socket calls during replay
113+
make_request("GET", "/test/toplevel-stream")
114+
115+
# BUG: Multipart file upload - triggers unpatched socket calls during replay
116+
make_request("POST", "/test/multipart-files")
117+
118+
# BUG: Follow redirects - replay returns wrong final_url and redirect_count
119+
make_request("GET", "/test/follow-redirects")
120+
121+
# BUG: AsyncClient.send() - triggers unpatched socket calls during replay
122+
make_request("GET", "/test/async-send")
123+
124+
# BUG: Async streaming - triggers unpatched socket calls during replay
125+
make_request("GET", "/test/async-stream")
126+
103127
print("\nAll requests completed successfully")

0 commit comments

Comments
 (0)