-
Notifications
You must be signed in to change notification settings - Fork 0
MPT-22959 Buffer streamed bodies before 401 retry in extension framework auth #366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
770013f
fed5e13
d0ad4ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,6 +40,12 @@ class ExtensionFrameworkAuthentication(Authentication): | |
| account scopes should construct one provider per scope. | ||
| """ | ||
|
|
||
| # The 401 retry re-sends the request, so one-shot streamed bodies (raw iterators, | ||
| # non-seekable files) must be buffered before the first send; otherwise the retry | ||
| # replays an already-consumed stream. httpx only honours this flag in the default | ||
| # ``auth_flow`` wrappers, so the overridden flows below also buffer explicitly. | ||
| requires_request_body = True | ||
|
|
||
| def __init__( | ||
| self, | ||
| secret: str, | ||
|
|
@@ -76,6 +82,7 @@ def sync_auth_flow( | |
| self, request: httpx.Request | ||
| ) -> Generator[httpx.Request, httpx.Response, None]: | ||
| """Attach a cached token, refreshing it proactively and on 401.""" | ||
| request.read() | ||
| if self._token is None or self._is_expired(): | ||
| self._refresh_sync() | ||
| request.headers["Authorization"] = f"Bearer {self._token}" | ||
|
|
@@ -90,6 +97,7 @@ async def async_auth_flow( | |
| self, request: httpx.Request | ||
| ) -> AsyncGenerator[httpx.Request, httpx.Response]: | ||
| """Attach a cached token, refreshing it proactively and on 401.""" | ||
| await request.aread() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: The async flow now always drains the full request stream into memory before sending, which can block or exhaust memory for large/long-lived async streams and removes expected streaming behavior for normal successful requests. Add size-aware buffering or skip replay logic for oversized/non-replayable streams to avoid forcing full in-memory reads. [performance] Severity Level: Major
|
||
| if self._token is None or self._is_expired(): | ||
| await self._refresh_async() | ||
| request.headers["Authorization"] = f"Bearer {self._token}" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: The new unconditional pre-read buffers every request body into memory before the first send, which breaks true streaming semantics and can cause very large uploads to consume excessive RAM (or fail with out-of-memory) even when no 401 retry happens. Restrict buffering to bounded-size bodies or add a safe fallback path for large/non-bufferable streams instead of always materializing the full payload. [performance]
Severity Level: Majorβ οΈ
Steps of Reproduction β
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent π€
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review β this behaviour is intentional, so we're keeping the unconditional buffering:
It is the deliberate trade-off of this fix. The goal of MPT-22959 is that the request body is read into memory before the auth flow so the 401 retry is always replayable. It is also exactly what httpx itself does for auth schemes that declare
requires_request_body = True: the default auth-flow wrapper performs the same unconditionalrequest.read(), with no size bound or fallback. Since this class overrides the flow wrappers, the explicitrequest.read()/await request.aread()just replicates the standard httpx convention.Restricting buffering to bounded-size bodies would reintroduce the bug. Large non-seekable streamed uploads are precisely the case where the 401 retry silently corrupts data (truncated multipart parts,
httpx.StreamConsumed). A size-capped or fallback path would trade silent data corruption for memory savings on exactly the requests that matter most β the wrong trade for an API client.The practical impact is limited. Typical bodies in this client (
json=, multipart with seekable files) are either already in memory or cheaply replayable; huge non-seekable streams are a marginal case, and the buffering behaviour is now documented indocs/usage.mdso callers can account for it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
β Customized review instruction saved!
Instruction:
Applied to:
mpt_api_client/auth/**π‘ To manage or update this instruction, visit: CodeAnt AI Settings