feat(http): implement body inspection handler - #736
RKS (rksharma-owg) wants to merge 13 commits into
Conversation
- Add BodyInspectionHandlerOption with inspect_request_body and inspect_response_body flags - Add BodyInspectionHandler middleware to non-destructively inspect request and response bodies - Provide byte accessors and rewound BytesIO stream helpers - Register BodyInspectionHandler in KiotaClientFactory default middleware pipeline - Add comprehensive unit tests covering options, request/response capture, streaming, and pipeline integration
This comment was marked as outdated.
This comment was marked as outdated.
|
RKS (@rksharma-owg) would you mind addressing the copilot comments please? |
|
Addressed the review findings in ee0ea54: standardized the telemetry key, preserved per-request options across redirects, restored replayable raw response streams after inspection, and added regressions for each behavior. The full Python 3.10-3.14 fork preflight passed, and the linked documentation update is MicrosoftDocs/openapi-docs#175. |
|
Addressed the new review in 1dc4c07. Reused per-request options now clear stale captures, and already-buffered responses retain their original consumed/closed raw-stream state. The request-stream concern was verified against HTTPX 0.25.0-0.28.1 and is already handled by |
|
Addressed both findings from the latest Copilot review in
Local validation is green ( |
|
I also addressed the decoded-stream lifecycle finding in |
There was a problem hiding this comment.
🔵 Needs a closer look
Address the two moderate issues involving unnecessary per-request allocations and closed-response handling.
Review details
Suppressed comments (2)
packages/http/httpx/kiota_http/middleware/body_inspection_handler.py:103
- Because this handler is now present in every default pipeline, these unconditional resets allocate a new
WeakKeyDictionarytwice for every request even when both inspection flags are disabled. That adds avoidable per-request overhead to the default client; initialize/update the context capture map only when inspection is enabled or there is existing state that must be cleared, while retaining the reset behavior for reused options.
# Clear body per request
current_options.request_body = None
current_options.response_body = None
packages/http/httpx/kiota_http/middleware/body_inspection_handler.py:69
- A response can be closed without being marked stream-consumed (
Response.aclose()setsis_closed), so this condition still callsaiter_raw()on an unreadable stream and raiseshttpx.StreamClosed. Treat closed responses like other unrecoverable streams and skip inspection instead of turning a valid downstream response into an exception.
elif not response.is_stream_consumed:
- Files reviewed: 8/8 changed files
- Comments generated: 0 new
- Review effort level: Lite
|
Thanks for the extra pass — both moderate findings are addressed in |
…n-handler # Conflicts: # packages/http/httpx/kiota_http/middleware/redirect_handler.py
… request.options in redirect handler
There was a problem hiding this comment.
🔵 Needs a closer look
Moderate telemetry and span-parent propagation issues remain unresolved.
Review details
Suppressed comments (3)
Previously missed (1) — in code that hasn't changed since the last review.
packages/http/httpx/kiota_http/middleware/body_inspection_handler.py:53
- This attribute is emitted as
trueeven when both inspection flags are at their documented default ofFalse, so spans falsely report body inspection as enabled on every default-pipeline request. The specification defines thisenableattribute as true only when the handler is enabled; set it from the request/response inspection flags (and add a disabled-path assertion).
packages/http/httpx/kiota_http/middleware/body_inspection_handler.py:99
- This new native-extension path can select the body option, but
_create_observability_spanstill reads only the legacyrequest.optionsattribute (BaseMiddleware._create_observability_span, lines 73-80). For extension-only requests such as the added redirect regression, the body-inspection span therefore loses theparent_spancarried inREQUEST_OPTIONS_KEYand is not nested under the request span; make the span helper consume the same extension bridge (while retaining the legacy fallback).
request_options = request.extensions.get(REQUEST_OPTIONS_KEY)
if request_options is None:
request_options = getattr(request, "options", None)
if request_options:
current_options = request_options.get(BodyInspectionHandlerOption.get_key(), None)
packages/http/httpx/kiota_http/middleware/redirect_handler.py:69
- The handler now accepts
REQUEST_OPTIONS_KEYrequest options, but both spans insendare created through_create_observability_span, which only reads the legacyrequest.optionsattribute. An extension-only request consequently loses its carriedparent_spanfor redirect telemetry; update the shared span lookup to read the native extension first and retain the legacy fallback.
request_options = request.extensions.get(REQUEST_OPTIONS_KEY)
if request_options is None:
request_options = getattr(request, "options", None)
- Files reviewed: 11/11 changed files
- Comments generated: 0 new
- Review effort level: Lite
| current_options = self._get_current_options(request) | ||
| request_options = request.extensions.get(REQUEST_OPTIONS_KEY) | ||
| if request_options is None: | ||
| request_options = getattr(request, "options", None) |
There was a problem hiding this comment.
we're still relying on reflection here, and a number of other places. Can you please clean it up?
There was a problem hiding this comment.
Cleaned up reflection across all middleware handlers (redirect_handler, body_inspection_handler, headers_inspection_handler, parameters_name_decoding_handler, retry_handler, url_replace_handler, user_agent_handler, and BaseMiddleware). All handlers now read options directly from HTTPX's native extension dictionary via request.extensions.get(REQUEST_OPTIONS_KEY), and monkey-patched request.options has been eliminated. All unit and middleware tests pass.
…dlers in favor of request extensions
|



Resolves #418
Description
Implements the
BodyInspectionHandlermiddleware andBodyInspectionHandlerOptionfor Kiota Python, following the shared middleware specification in microsoftgraph/msgraph-sdk-design#116.Summary of Changes
BytesIOaccessors.BodyInspectionHandlerin the default middleware pipeline.TypeErrorbefore option or span access.com.microsoft.kiota.handler.bodyInspection.enabletelemetry attribute and keeps the span open through inspection, transport execution, response handling, and exception cleanup.Documentation
Verification
pytestinpackages/http/httpx: 154 passed.suggestion-modeconfiguration warning.eaa50db: https://github.com/rksharma-owg/kiota-python/actions/runs/35131242553eaa50db.