Fix cancelled HTTP requests showing as Pending in DevTools Network tab#9685
Fix cancelled HTTP requests showing as Pending in DevTools Network tab#9685rishika0212 wants to merge 11 commits intoflutter:masterfrom
Conversation
srawlins
left a comment
There was a problem hiding this comment.
This code looks great; thanks for the PR! I'd like to test manually before landing.
| super.shrinkWrap, | ||
| super.padding, | ||
| super.scrollCacheExtent, | ||
| super.cacheExtent, |
There was a problem hiding this comment.
CC @elliette ; from the PR description:
Update CustomPointerScrollView to use cacheExtent so the project compiles with the current Flutter SDK
packages/devtools_app/lib/src/shared/http/http_request_data.dart
Outdated
Show resolved
Hide resolved
packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart
Show resolved
Hide resolved
|
Hi @rishika0212 sorry for the delay. I've been manually testing this and I have a clarifying question: I use the devtools_companion app to test the Network panel. I've got a PR that adds "cancellation" support: elliette/devtools_companion#23. I've observed behavior that seems questionable to me, but you might shed some light. Take this series of steps:
After these steps, I do not see the Cancelled status (it seems to remain Open). However, if the server never starts to send a response (the "Response Body checkbox is not checked), then the Cancelled status appears as expected. I think the Cancelled status should still appear, even if the server has started to respond. But I am not an HTTP expert. WDYT? |
|
Oh, additionally, the other problem I observed is that the table will always show the Cancelled status before a server completes its response, even if the request was not in fact cancelled. I'll double check when this behavior occurs, but I observed it in the DevTools Companion app. |
|
Hey @srawlins thanks again for the detailed repro, this was super helpful. I ran through it carefully using devtools_companion (including your srawlins/cancel case) and your exact sequence. Here’s what I’m seeing:
I also tightened up the DevTools logic a bit to avoid showing “Cancelled” too early before a response actually completes. For the specific edge case you mentioned (response starts, then gets cancelled after ~1s): However, from what I can tell, this case doesn’t always come through with a clear cancellation signal in the HTTP profile data. Sometimes it still ends up looking like a normal 200 style response, which makes it tricky for DevTools to classify it confidently based on the current fields. So overall from what I can tell…
Let me know if you’re seeing anything different on your side! |
Do you mean you've done this before today, or you will upload a commit? |
Before your comment, I hadn’t specifically tested the “cancel before complete” path in this depth, but after your note I ran those scenarios in devtools_companion, improved the classification logic, and added tests. I can push the tested improvements now (mainly the cancelled vs pending behavior). The “response already started, then cancelled” case is still not fully reliable yet since the profiling signal is ambiguous in some runs. |
|
Right now, in the “response started, then client cancels” case, the profile payload can sometimes look like a normal 200 response. When that happens, the UI doesn’t really have a reliable way to tell the difference between:
So unless we get a clear signal from upstream (like wasCancelled, a dedicated cancel event, or a specific error field), we’re basically guessing. And that guess can go wrong in both directions:
|
That would be great!
This sounds OK to me. |
I've pushed the updates. |
srawlins
left a comment
There was a problem hiding this comment.
Just a couple of small changes; thanks!
| 'cancel', | ||
| 'canceled', | ||
| 'cancelled', | ||
| 'operation canceled', |
There was a problem hiding this comment.
Since we are just searching in a String for these values, we either don't need the longer ones ("operation canceled") or we don't need the shorter ones ("canceled").
I think it is find to just keep the shorter ones.
Are these values derived from some general libraries, like the dio package and dart:io?
There was a problem hiding this comment.
Yes, they’re derived from cancellation wording observed in common Dart HTTP stacks, specifically our dart:io, package:http, and dio companion flows and the resulting VM profile payload strings.
| 'cancelled', | ||
| 'operation canceled', | ||
| 'operation cancelled', | ||
| 'abort', |
There was a problem hiding this comment.
I think we should remove the shortest two in this list, "cancel" and "abort." Unless there are examples from dart:io, package:http, or package:dio, where a cancelled error/event contains "cancel" or "abort" but not "cancelled"/"canceled" or "aborted."
There was a problem hiding this comment.
Thanks for the suggestions, I’ve updated things accordingly.
I kept just canceled, cancelled, and aborted, and removed the longer phrases like operation canceled / operation cancelled since we’re already doing substring matching. I also dropped the more generic terms like cancel and abort to avoid false positives.
These markers are based on the cancellation wording we’ve seen in related flows (like dart:io request.abort(), package:http client.close(), and dio cancelToken.cancel(...)) along with the profiler payload text.
One thing to note: in companion cancel mode, we can still get a 200 since the status gets set early so in those cases, classification relies on explicit cancellation text in the error/event payloads.
| DateTime? get _endTime => | ||
| _hasError ? _request.endTime : _request.response?.endTime; | ||
|
|
||
| static const _cancellationMarkers = [ |
There was a problem hiding this comment.
Can you add a doc comment above this. I'd be nice if it included how the list is to be used, and how it was derived.
| DateTime? get _endTime => | ||
| _hasError ? _request.endTime : _request.response?.endTime; | ||
|
|
||
| static const _cancellationMarkers = [ |
There was a problem hiding this comment.
Since this is a private static field used only one time, by a private method, it might be nice to just inline this list into the method itself.
|
|
||
| bool _matchesCancellationMarker(String? value) { | ||
| final normalized = value?.toLowerCase(); | ||
| if (normalized == null) return false; |
There was a problem hiding this comment.
The only way that normalized can be null is if value is null, so we can check that before the toLowerCase code.
| return end?.difference(start); | ||
| } | ||
|
|
||
| // Cancelled request |
There was a problem hiding this comment.
This comment doesn't really add any information.
| ## Network profiler updates | ||
|
|
||
| - Added a filter setting to hide HTTP-profiler socket data. [#9698](https://github.com/flutter/devtools/pull/9698) | ||
| - Improve HTTP request status classification in the Network tab to better distinguish cancelled, completed, and in-flight requests (for example, avoiding some cases where cancelled requests appeared as pending). (#9683) |
There was a problem hiding this comment.
Don't replace the "filter setting" release note; just add yours below it, as a second bullet point.
|
|
||
| ## App size tool updates | ||
|
|
||
| - Added documentation links and improved handling for null files. [#9689](https://github.com/flutter/devtools/pull/9689) |
There was a problem hiding this comment.
Don't remove this text either.
| expect(httpMethods.contains(request.method), true); | ||
| expect(request.status, request.inProgress ? isNull : isNotNull); | ||
| if (request.inProgress) { | ||
| expect(request.status, isNull); |
There was a problem hiding this comment.
This is clearer than the old code, thanks!
Description
This PR fixes an issue where cancelled HTTP requests appear as "Pending" in the DevTools Network tab.
When a request is aborted (for example using
HttpClientRequest.abortor Dio cancellation), DevTools keeps the request in a Pending state because no response is received. This change detects such cases and displays the request status as "Cancelled" instead.Changes
HttpRequestDatainProgressnullfor cancelled requestsCustomPointerScrollViewto usecacheExtentso the project compiles with the current Flutter SDKAll existing network tests pass locally.
Fixes: #9593
Pre-launch Checklist
If you need help, consider asking for help on Discord.