diff --git a/lib/network/components/manager/script_manager.dart b/lib/network/components/manager/script_manager.dart index 4308e9c..238773a 100644 --- a/lib/network/components/manager/script_manager.dart +++ b/lib/network/components/manager/script_manager.dart @@ -247,7 +247,7 @@ async function onResponse(context, request, response) { for (var item in list) { if (item.enabled && item.match(url)) { var context = jsonEncode(scriptContext(item)); - var jsRequest = jsonEncode(convertJsRequest(request)); + var jsRequest = jsonEncode(await convertJsRequest(request)); String script = await getScript(item); var jsResult = await flutterJs.evaluateAsync( """var request = $jsRequest, context = $context; request['scriptContext'] = context; $script\n onRequest(context, request)"""); @@ -275,7 +275,7 @@ async function onResponse(context, request, response) { if (item.enabled && item.match(url)) { var context = jsonEncode(request.attributes['scriptContext'] ?? scriptContext(item)); var jsRequest = jsonEncode(convertJsRequest(request)); - var jsResponse = jsonEncode(convertJsResponse(response)); + var jsResponse = jsonEncode(await convertJsResponse(response)); String script = await getScript(item); var jsResult = await flutterJs.evaluateAsync( """var response = $jsResponse, context = $context; response['scriptContext'] = context; $script @@ -321,7 +321,7 @@ async function onResponse(context, request, response) { } //转换js request - Map convertJsRequest(HttpRequest request) { + Future> convertJsRequest(HttpRequest request) async { var requestUri = request.requestUri; return { 'host': requestUri?.host, @@ -330,17 +330,18 @@ async function onResponse(context, request, response) { 'queries': requestUri?.queryParameters, 'headers': request.headers.toMap(), 'method': request.method.name, - 'body': request.getBodyString(), + 'body': request.decodeBodyString(), 'rawBody': request.body }; } //转换js response - Map convertJsResponse(HttpResponse response) { - dynamic body = response.getBodyString(); + Future> convertJsResponse(HttpResponse response) async { + dynamic body = await response.decodeBodyString(); if (response.contentType.isBinary) { body = response.body; } + return { 'headers': response.headers.toMap(), 'statusCode': response.status.code, diff --git a/lib/network/components/request_rewrite.dart b/lib/network/components/request_rewrite.dart index be01f3a..339a0fc 100644 --- a/lib/network/components/request_rewrite.dart +++ b/lib/network/components/request_rewrite.dart @@ -176,9 +176,9 @@ class RequestRewriteInterceptor extends Interceptor { } //修改消息 - _updateMessage(HttpMessage message, RewriteItem item) { + _updateMessage(HttpMessage message, RewriteItem item) async { if (item.type == RewriteType.updateBody && message.body != null) { - String body = message.getBodyString().replaceAllMapped(RegExp(item.key!), (match) { + String body = (await message.decodeBodyString()).replaceAllMapped(RegExp(item.key!), (match) { if (match.groupCount > 0 && item.value?.contains("\$1") == true) { return item.value!.replaceAll("\$1", match.group(1)!); } diff --git a/lib/network/http/codec.dart b/lib/network/http/codec.dart index b5f0b5c..b63d9b0 100644 --- a/lib/network/http/codec.dart +++ b/lib/network/http/codec.dart @@ -24,8 +24,8 @@ import 'package:proxypin/network/http/constants.dart'; import 'package:proxypin/network/http/h2/codec.dart'; import 'package:proxypin/network/http/http_parser.dart'; import 'package:proxypin/network/util/byte_buf.dart'; +import 'package:proxypin/network/util/compress.dart'; -import '../../utils/compress.dart'; import 'http.dart'; import 'http_headers.dart'; diff --git a/lib/network/http/http.dart b/lib/network/http/http.dart index 3da210a..8189673 100644 --- a/lib/network/http/http.dart +++ b/lib/network/http/http.dart @@ -20,9 +20,9 @@ import 'dart:math'; import 'package:proxypin/network/channel/host_port.dart'; import 'package:proxypin/network/http/content_type.dart'; import 'package:proxypin/network/http/websocket.dart'; +import 'package:proxypin/network/util/compress.dart'; import 'package:proxypin/network/util/logger.dart'; import 'package:proxypin/network/util/process_info.dart'; -import 'package:proxypin/utils/compress.dart'; import 'http_headers.dart'; @@ -52,7 +52,9 @@ abstract class HttpMessage { //报文大小 int? packageSize; - List? body; + List? _body; + String? _bodyString; + String? remoteHost; int? remotePort; @@ -79,6 +81,14 @@ abstract class HttpMessage { orElse: () => const MapEntry("unknown", ContentType.http)) .value; + List? get body => _body; + + set body(List? body) { + _body = body; + _bodyString = null; + packageSize = body?.length ?? 0; + } + ///获取消息体编码 String? get charset { var contentType = headers.contentType; @@ -100,6 +110,10 @@ abstract class HttpMessage { return ""; } + if (_bodyString != null) { + return _bodyString!; + } + charset ??= this.charset; try { List rawBody = body!; @@ -116,6 +130,29 @@ abstract class HttpMessage { } } + Future decodeBodyString() async { + if (body == null || body?.isEmpty == true) { + return ""; + } + + if (_bodyString != null) { + return _bodyString!; + } + + List rawBody = body!; + if (headers.contentEncoding == 'zstd') { + rawBody = await zstdDecode(body!) ?? []; + if (charset == 'utf-8' || charset == 'utf8') { + _bodyString = utf8.decode(rawBody); + } else { + _bodyString = String.fromCharCodes(rawBody); + } + return _bodyString!; + } + + return getBodyString(); + } + String get cookie => headers.cookie; List messages = []; diff --git a/lib/utils/compress.dart b/lib/network/util/compress.dart similarity index 64% rename from lib/utils/compress.dart rename to lib/network/util/compress.dart index df53ed0..0c44c5a 100644 --- a/lib/utils/compress.dart +++ b/lib/network/util/compress.dart @@ -1,7 +1,9 @@ import 'dart:io'; +import 'dart:typed_data'; import 'package:brotli/brotli.dart'; import 'package:proxypin/network/util/logger.dart'; +import 'package:zstandard/zstandard.dart'; ///GZIP 解压缩 List gzipDecode(List byteBuffer) { @@ -28,3 +30,14 @@ List brDecode(List byteBuffer) { return byteBuffer; } } + +///zstd 解压缩 +Future?> zstdDecode(List byteBuffer) async { + final zstandard = Zstandard(); + try { + return zstandard.decompress(Uint8List.fromList(byteBuffer)); + } catch (e) { + logger.e("zstdDecode error: $e"); + return byteBuffer; + } +} diff --git a/lib/ui/component/utils.dart b/lib/ui/component/utils.dart index 7d3552a..96f5285 100644 --- a/lib/ui/component/utils.dart +++ b/lib/ui/component/utils.dart @@ -161,10 +161,15 @@ void unSelect(EditableTextState editableTextState) { } ///Future -Widget futureWidget(Future future, Widget Function(T data) toWidget, {bool loading = false}) { +Widget futureWidget(Future future, Widget Function(T data) toWidget, {T? initialData, bool loading = false}) { return FutureBuilder( future: future, + initialData: initialData, builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.data != null) { + return toWidget(snapshot.requireData); + } + if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { logger.e(snapshot.error); diff --git a/lib/ui/content/body.dart b/lib/ui/content/body.dart index 1c89b4e..ffa20c7 100644 --- a/lib/ui/content/body.dart +++ b/lib/ui/content/body.dart @@ -174,8 +174,8 @@ class HttpBodyState extends State { : IconButton( icon: Icon(Icons.copy, size: 18), tooltip: localizations.copy, - onPressed: () { - var body = bodyKey.currentState?.body; + onPressed: () async { + var body = await bodyKey.currentState?.getBody(); if (body == null) { return; } @@ -197,8 +197,11 @@ class HttpBodyState extends State { list.add(IconButton( icon: const Icon(Icons.text_format, size: 21), tooltip: localizations.encode, - onPressed: () { - encodeWindow(EncoderType.base64, context, bodyKey.currentState?.body); + onPressed: () async { + var body = await bodyKey.currentState?.getBody(); + if (mounted) { + encodeWindow(EncoderType.base64, context, body); + } })); if (!inNewWindow) { list.add(const SizedBox(width: 3)); @@ -339,7 +342,7 @@ class _BodyState extends State<_Body> { return _getBody(viewType); } - String? get body { + Future getBody() async { if (message?.isWebSocket == true) { return message?.messages.map((e) => e.payloadDataAsString).join("\n"); } @@ -351,17 +354,19 @@ class _BodyState extends State<_Body> { if (viewType == ViewType.hex) { return message!.body!.map(intToHex).join(" "); } + try { if (viewType == ViewType.formUrl) { return Uri.decodeFull(message!.bodyAsString); } + if (viewType == ViewType.jsonText || viewType == ViewType.json) { //json格式化 - var jsonObject = json.decode(message!.bodyAsString); + var jsonObject = json.decode(await message!.decodeBodyString()); return const JsonEncoder.withIndent(" ").convert(jsonObject); } } catch (_) {} - return message!.bodyAsString; + return message!.decodeBodyString(); } Widget _getBody(ViewType type) { @@ -397,37 +402,40 @@ class _BodyState extends State<_Body> { return const SizedBox(); } - try { - if (type == ViewType.jsonText) { - var jsonObject = json.decode(message!.bodyAsString); - return JsonText( - json: jsonObject, - indent: Platforms.isDesktop() ? ' ' : ' ', - colorTheme: ColorTheme.of(Theme.of(context).brightness), - scrollController: widget.scrollController); - } - - if (type == ViewType.json) { - return JsonViewer(json.decode(message!.bodyAsString), colorTheme: ColorTheme.of(Theme.of(context).brightness)); - } - - if (type == ViewType.formUrl) { - return SelectableText(Uri.decodeFull(message!.bodyAsString), contextMenuBuilder: contextMenu); - } - if (type == ViewType.image) { - return Center(child: Image.memory(Uint8List.fromList(message?.body ?? []), fit: BoxFit.scaleDown)); - } - if (type == ViewType.video) { - return const Center(child: Text("video not support preview")); - } - if (type == ViewType.hex) { - return SelectableText(message!.body!.map(intToHex).join(" "), contextMenuBuilder: contextMenu); - } - } catch (e) { - logger.e(e, stackTrace: StackTrace.current); + if (type == ViewType.image) { + return Center(child: Image.memory(Uint8List.fromList(message?.body ?? []), fit: BoxFit.scaleDown)); + } + if (type == ViewType.video) { + return const Center(child: Text("video not support preview")); + } + if (type == ViewType.hex) { + return SelectableText(message!.body!.map(intToHex).join(" "), contextMenuBuilder: contextMenu); } - return SelectableText.rich(TextSpan(text: message?.bodyAsString), contextMenuBuilder: contextMenu); + if (type == ViewType.formUrl) { + return SelectableText(Uri.decodeFull(message!.getBodyString()), contextMenuBuilder: contextMenu); + } + + return futureWidget(message!.decodeBodyString(), initialData: message!.getBodyString(), (body) { + try { + if (type == ViewType.jsonText) { + var jsonObject = json.decode(body); + return JsonText( + json: jsonObject, + indent: Platforms.isDesktop() ? ' ' : ' ', + colorTheme: ColorTheme.of(Theme.of(context).brightness), + scrollController: widget.scrollController); + } + + if (type == ViewType.json) { + return JsonViewer(json.decode(body), colorTheme: ColorTheme.of(Theme.of(context).brightness)); + } + } catch (e) { + logger.e(e, stackTrace: StackTrace.current); + } + + return SelectableText.rich(TextSpan(text: body), contextMenuBuilder: contextMenu); + }); } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index b1f65f6..6a2f742 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -13,6 +13,7 @@ #include #include #include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) desktop_multi_window_registrar = @@ -36,4 +37,7 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) window_manager_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); window_manager_plugin_register_with_registrar(window_manager_registrar); + g_autoptr(FlPluginRegistrar) zstandard_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ZstandardLinuxPlugin"); + zstandard_linux_plugin_register_with_registrar(zstandard_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 740dc77..ec5113d 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -10,6 +10,7 @@ list(APPEND FLUTTER_PLUGIN_LIST screen_retriever_linux url_launcher_linux window_manager + zstandard_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 69d2aa9..76534dc 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -17,6 +17,7 @@ import share_plus import shared_preferences_foundation import url_launcher_macos import window_manager +import zstandard_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterMultiWindowPlugin.register(with: registry.registrar(forPlugin: "FlutterMultiWindowPlugin")) @@ -31,4 +32,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) + ZstandardMacosPlugin.register(with: registry.registrar(forPlugin: "ZstandardMacosPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index 5f40cda..fa6bc67 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,7 +43,10 @@ dependencies: qr_flutter: ^4.1.0 flutter_qr_reader_plus: ^1.0.6 + brotli: ^0.6.0 + zstandard: ^1.3.27 + win32audio: ^1.3.1 vclibs: ^0.1.3 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index bde187d..bb715bd 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -18,6 +18,7 @@ #include #include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { DesktopMultiWindowPluginRegisterWithRegistrar( @@ -44,4 +45,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("WindowManagerPlugin")); WindowsSingleInstancePluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("WindowsSingleInstancePlugin")); + ZstandardWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ZstandardWindowsPluginCApi")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 370786e..ce4c139 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -15,6 +15,7 @@ list(APPEND FLUTTER_PLUGIN_LIST win32audio window_manager windows_single_instance + zstandard_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST