perf: trim body bytes before UTF-8 decoding - #109
Merged
Merged
Conversation
Bodies were decoded in full (up to 512 KB) only to keep the first 32K characters of the panel preview. Clip the byte prefix to maxChars * 4 + 3 before utf8.decode so the tail that truncation discards is never decoded (~1/16 work for ASCII, ~1/4 for CJK); preview content is unchanged and the base64/hex binary fallback still reads the original bytes. Bump version to 1.13.0 (pubspec / podspec / InspectorVersion / both READMEs / CHANGELOG) and regenerate docs/.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary / 摘要
Trim request/response body bytes to the panel's preview cap before UTF-8 decoding, so a 512 KB body is no longer decoded in full just to keep the first 32K characters. / 在 UTF-8 解码前按面板预览上限裁剪请求 / 响应体字节,不再为保留前 32K 字符而完整解码 512 KB 的 body。
Changes / 变更
_bytesForPreviewDecodeinhttp_interceptor.dart(shared by both part files): clips the byte prefix tomaxChars * 4 + 3— one UTF-8 char is at most 4 bytes, plus 3 so a multi-byte char is never cut in half. / 在http_interceptor.dart新增_bytesForPreviewDecode(两个 part 共用):把字节前缀裁剪到maxChars * 4 + 3—— UTF-8 单字符最多 4 字节,额外 3 字节确保不会把多字节字符切在中间。_decodeBodyOrHexPreviewtakesmaxCharsand decodes only the trimmed prefix; the base64 / hex binary fallback still reads the original bytes, so binary responses behave exactly as before. / 响应侧:_decodeBodyOrHexPreview新增maxChars,只解码裁剪后的前缀;base64 / hex 的二进制兜底仍基于原始字节,二进制响应行为完全不变。inspector_http_client.dart): same trimming beforeutf8.decodefor captured request bodies. / 请求侧(inspector_http_client.dart):请求体解码前同样剪枝。InspectorService.maxBodyPreviewBytesgetter so interceptors can read the configured preview cap (it used to be private and invisible to them). / 新增InspectorService.maxBodyPreviewBytesgetter,让拦截器能读取配置的预览上限(原来该上限私有、拦截器无法感知)。1.13.0acrosspubspec.yaml,ios/zero_inspector_kit.podspec,InspectorVersion.value, both READMEs (install snippets + upgrade callout) andCHANGELOG.md; regeneratedocs/viasync-docs.mjs. / 版本升至1.13.0,同步pubspec.yaml、ios/zero_inspector_kit.podspec、InspectorVersion.value、两个 README(安装片段与升级提示)与CHANGELOG.md;并通过sync-docs.mjs重新生成docs/。Context / 背景
The interceptor buffers up to 512 KB of a body, but
NetworkRequest.copyWith(maxBodyBytes:)truncates the preview to 32K characters — so the decode paid for a tail that is discarded immediately (up to ~16x wasted work for ASCII, ~4x for CJK). That decode is synchronous on the UI isolate, and it was the only realistic way the inspector could cost the host app a frame on large responses. / 拦截器最多缓冲 512 KB body,而NetworkRequest.copyWith(maxBodyBytes:)会把预览截断到 32K 字符 —— 解码成本全花在随即被丢弃的尾部(纯 ASCII 最多浪费约 16 倍,中文约 4 倍)。该解码同步发生在 UI isolate 上,是检查器在超大响应时唯一可能让宿主 App 掉一帧的地方。An extra isolate was considered and rejected: Flutter isolates share no memory, so the cross-isolate copy costs as much as (or more than) the decode it saves, and the capture probes —
HttpOverrides,debugPrintoverride,addTimingsCallback,NavigatorObserver— must live in the main isolate anyway. Preview content and length are unchanged by this PR. / 曾评估过另开 isolate,已否决:Flutter isolate 之间不共享内存,跨 isolate 拷贝的成本不低于省下的解码成本,而且采集探针(HttpOverrides、debugPrint覆写、addTimingsCallback、NavigatorObserver)本身必须在主 isolate。本次改动不改变预览内容与长度。Checklist / 检查项
Test plan
dart format .— 113 files, 0 changedflutter analyze— No issues foundflutter test— 285 passed (incl.version_consistency_test)dart run tool/check_release.dart— all version references consistent with 1.13.0[… truncated N chars …]marker / 手工:发起一个大于 512 KB 的响应,确认面板仍显示前 32K 字符与[… truncated N chars …]提示🤖 Generated with Zero Buddy