From 9aba03cfcbf3520beff0693826032f6490291809 Mon Sep 17 00:00:00 2001 From: wanghongenpin Date: Sun, 17 Dec 2023 15:36:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81head=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=E6=89=8B=E6=9C=BA=E7=AB=AF=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E9=87=8D=E5=86=99=E5=88=87=E6=8D=A2=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=E5=8E=9F=E5=A7=8B=E7=9A=84=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/network/channel.dart | 16 ++++--- lib/network/http/codec.dart | 10 ++-- lib/network/http/http.dart | 3 +- lib/network/http/websocket.dart | 18 +------ lib/network/network.dart | 11 +++-- lib/network/util/tls.dart | 58 ++++++++++++++++++++++- lib/storage/favorites.dart | 4 +- lib/ui/mobile/request/request_editor.dart | 48 +++++++++++-------- macos/Runner.xcodeproj/project.pbxproj | 3 ++ macos/Runner/RunnerProfile.entitlements | 2 +- pubspec.yaml | 2 +- 11 files changed, 119 insertions(+), 56 deletions(-) diff --git a/lib/network/channel.dart b/lib/network/channel.dart index a3cf4a9..13cb2e1 100644 --- a/lib/network/channel.dart +++ b/lib/network/channel.dart @@ -84,6 +84,8 @@ class Channel { pipeline.listen(this); } + String? get selectedProtocol => isSsl ? (_socket as SecureSocket).selectedProtocol : null; + ///是否是ssl链接 bool get isSsl => _socket is SecureSocket; @@ -228,10 +230,12 @@ class ChannelPipeline extends ChannelHandler { return; } - var data = _decoder.decode(buffer); + HttpRequest? request = remoteChannel?.getAttribute(AttributeKeys.request); + var data = _decoder.decode(buffer, resolveBody: request?.method != HttpMethod.head); if (data == null) { return; } + // print(String.fromCharCodes(buffer.buffer)); var length = buffer.length; buffer.clear(); @@ -247,14 +251,14 @@ class ChannelPipeline extends ChannelHandler { if (data is HttpResponse) { data.packageSize = length; data.remoteAddress = '${channel.remoteAddress.host}:${channel.remotePort}'; - data.request = remoteChannel?.getAttribute(AttributeKeys.request); - data.request?.response = data; + data.request = request; + request?.response = data; } //websocket协议 if (data is HttpResponse && data.isWebSocket && remoteChannel != null) { - data.request?.hostAndPort?.scheme = channel.isSsl ? HostAndPort.wssScheme : HostAndPort.wsScheme; - print("webSocket ${data.request?.hostAndPort}"); + request?.hostAndPort?.scheme = channel.isSsl ? HostAndPort.wssScheme : HostAndPort.wsScheme; + logger.d("webSocket ${data.request?.hostAndPort}"); remoteChannel.write(data); var rawCodec = RawCodec(); @@ -284,7 +288,7 @@ class ChannelPipeline extends ChannelHandler { class RawCodec extends Codec { @override - Object? decode(ByteBuf data) { + Object? decode(ByteBuf data, {bool resolveBody = true}) { return data.readBytes(data.readableBytes()); } diff --git a/lib/network/http/codec.dart b/lib/network/http/codec.dart index 04f24ae..b197e75 100644 --- a/lib/network/http/codec.dart +++ b/lib/network/http/codec.dart @@ -113,7 +113,7 @@ class ByteBuf { /// 解码 abstract interface class Decoder { /// 解码 如果返回null说明数据不完整 - T? decode(ByteBuf byteBuf); + T? decode(ByteBuf byteBuf, {bool resolveBody = true}); } /// 编码 @@ -139,7 +139,7 @@ abstract class HttpCodec implements Codec { T createMessage(List reqLine); @override - T? decode(ByteBuf data) { + T? decode(ByteBuf data, {bool resolveBody = true}) { //请求行 if (_state == State.readInitial) { init(); @@ -156,10 +156,10 @@ abstract class HttpCodec implements Codec { //请求体 if (_state == State.body) { - var result = bodyReader!.readBody(data.readBytes(data.readableBytes())); - if (result.isDone) { + var result = resolveBody ? bodyReader!.readBody(data.readBytes(data.readableBytes())) : null; + if (!resolveBody || result?.isDone == true) { _state = State.done; - message.body = result.body; + message.body = result?.body; } } diff --git a/lib/network/http/http.dart b/lib/network/http/http.dart index e8c28b9..4d17d0e 100644 --- a/lib/network/http/http.dart +++ b/lib/network/http/http.dart @@ -18,6 +18,7 @@ import 'dart:convert'; import 'package:network_proxy/network/host_port.dart'; import 'package:network_proxy/network/http/websocket.dart'; +import 'package:network_proxy/network/util/logger.dart'; import 'package:network_proxy/utils/compress.dart'; import 'http_headers.dart'; @@ -272,7 +273,7 @@ enum HttpMethod { try { return HttpMethod.values.firstWhere((element) => element.name == name.toUpperCase()); } catch (error) { - print("$name :$error"); + logger.e("HttpMethod error $name :$error"); rethrow; } } diff --git a/lib/network/http/websocket.dart b/lib/network/http/websocket.dart index 20fc160..baf39e2 100644 --- a/lib/network/http/websocket.dart +++ b/lib/network/http/websocket.dart @@ -68,23 +68,9 @@ class WebSocketFrame { ///websocket 解码器 class WebSocketDecoder { - // Add a buffer to store incomplete data - final buffer = BytesBuilder(); - WebSocketFrame? decode(Uint8List byteBuf) { - // Add the new data to the buffer - buffer.add(byteBuf); - - // Try to parse a WebSocket frame from the buffer - var data = buffer.toBytes(); - if (canParseWebSocketFrame(data)) { - var frame = _parseWebSocketFrame(data); - - buffer.clear(); - return frame; - } - - return null; + var frame = _parseWebSocketFrame(byteBuf); + return frame; } bool canParseWebSocketFrame(Uint8List data) { diff --git a/lib/network/network.dart b/lib/network/network.dart index 2d719f6..70079cb 100644 --- a/lib/network/network.dart +++ b/lib/network/network.dart @@ -98,10 +98,6 @@ class Network { channel.putAttribute(AttributeKeys.domain, hostAndPort?.host); Channel? remoteChannel = channel.getAttribute(channel.id); - if (remoteChannel != null) { - remoteChannel.secureSocket = await SecureSocket.secure(remoteChannel.socket, - host: hostAndPort?.host, onBadCertificate: (certificate) => true); - } if (HostFilter.filter(hostAndPort?.host)) { remoteChannel = remoteChannel ?? await HttpClients.startConnect(hostAndPort!, RelayHandler(channel)); @@ -110,6 +106,13 @@ class Network { return; } + if (remoteChannel != null && !remoteChannel.isSsl) { + // var supportProtocols = TLS.supportProtocols(data); + remoteChannel.secureSocket = await SecureSocket.secure(remoteChannel.socket, + host: hostAndPort?.host, onBadCertificate: (certificate) => true); + } + + // var selectedProtocol = remoteChannel?.selectedProtocol; //ssl自签证书 var certificate = await CertificateManager.getCertificateContext(hostAndPort!.host); //服务端等待客户端ssl握手 diff --git a/lib/network/util/tls.dart b/lib/network/util/tls.dart index bfd6a0f..94b0bdb 100644 --- a/lib/network/util/tls.dart +++ b/lib/network/util/tls.dart @@ -17,6 +17,62 @@ import 'dart:typed_data'; class TLS { + ///从TLS Client Hello 获取支持的协议 + static List? supportProtocols(Uint8List data) { + try { + int sessionLength = data[43]; + int pos = 44 + sessionLength; + if (data.length < pos + 2) return null; + + int cipherSuitesLength = data.buffer.asByteData().getUint16(pos); + pos += 2 + cipherSuitesLength; + if (data.length < pos + 1) return null; + + int compressionMethodsLength = data[pos]; + pos += 1 + compressionMethodsLength; + if (data.length < pos + 2) return null; + + int extensionsLength = data.buffer.asByteData().getUint16(pos); + pos += 2; + if (data.length < pos + extensionsLength) return null; + + List protocols = []; + + int end = pos + extensionsLength; + while (pos + 4 <= end) { + int extensionType = data.buffer.asByteData().getUint16(pos); + int extensionLength = data.buffer.asByteData().getUint16(pos + 2); + pos += 4; + + if (extensionType == 16 /* ALPN */) { + if (pos + 2 > end) return protocols; + int alpnExtensionLength = data.buffer.asByteData().getUint16(pos); + pos += 2; + if (pos + alpnExtensionLength > end) return protocols; + + int alpnEnd = pos + alpnExtensionLength; + while (pos + 1 <= alpnEnd) { + int protocolLength = data[pos]; + pos += 1; + if (pos + protocolLength > alpnEnd) return protocols; + + String protocol = String.fromCharCodes(data.sublist(pos, pos + protocolLength)); + protocols.add(protocol); + + pos += protocolLength; + } + } else { + pos += extensionLength; + } + } + return protocols; + } catch (_) { + // Ignore errors, just return empty list + } + + return null; + } + ///判断是否是TLS Client Hello static bool isTLSClientHello(Uint8List data) { if (data.length < 43) return false; @@ -70,7 +126,7 @@ class TLS { } } } catch (_) { - // Ignore errors, just return null +// Ignore errors, just return null } return null; diff --git a/lib/storage/favorites.dart b/lib/storage/favorites.dart index 88c3815..0218b5e 100644 --- a/lib/storage/favorites.dart +++ b/lib/storage/favorites.dart @@ -16,7 +16,9 @@ class FavoriteStorage { var file = await _path; if (await file.exists()) { var value = await file.readAsString(); - + if (value.isEmpty) { + return list!; + } try { var config = jsonDecode(value) as List; for (var element in config) { diff --git a/lib/ui/mobile/request/request_editor.dart b/lib/ui/mobile/request/request_editor.dart index 791f094..b836452 100644 --- a/lib/ui/mobile/request/request_editor.dart +++ b/lib/ui/mobile/request/request_editor.dart @@ -167,6 +167,12 @@ class _HttpState extends State<_HttpWidget> with AutomaticKeepAliveClientMixin { return body; } + @override + void initState() { + super.initState(); + body = widget.message?.bodyAsString; + } + HttpHeaders? getHeaders() { return headerKey.currentState?.getHeaders(); } @@ -174,12 +180,14 @@ class _HttpState extends State<_HttpWidget> with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { super.build(context); - body = widget.message?.bodyAsString; - headerKey.currentState?.refreshHeader(widget.message?.headers); if (widget.message == null && widget.readOnly) { return const Center(child: Text("无数据")); } + if (widget.readOnly) { + body = widget.message?.bodyAsString; + headerKey.currentState?.refreshHeader(widget.message?.headers); + } return SingleChildScrollView( padding: const EdgeInsets.all(15), @@ -322,24 +330,24 @@ class HeadersState extends State { @override Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox( - width: double.infinity, - child: Text("Headers", style: TextStyle(fontWeight: FontWeight.w500, color: Colors.blue))), - const SizedBox(height: 10), - ...buildHeaders(), - widget.readOnly - ? const SizedBox() - : Container( - alignment: Alignment.center, - child: TextButton( - onPressed: () { - modifyHeader("", ""); - }, - child: const Text("添加Header", textAlign: TextAlign.center))) //添加按钮 - ])); + return ExpansionTile( + title: const Text("Headers", style: TextStyle(fontWeight: FontWeight.w500, color: Colors.blue)), + tilePadding: const EdgeInsets.only(left: 0, top: 10, bottom: 10), + initiallyExpanded: true, + shape: const Border(), + children: [ + ...buildHeaders(), + widget.readOnly + ? const SizedBox() + : Container( + alignment: Alignment.center, + child: TextButton( + onPressed: () { + modifyHeader("", ""); + }, + child: const Text("添加Header", textAlign: TextAlign.center))) //添加按钮 + ], + ); } List buildHeaders() { diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 6b4c395..ee43f81 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -619,6 +619,7 @@ ); MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.proxy.pin; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; @@ -759,6 +760,7 @@ ); MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.proxy.pin; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -787,6 +789,7 @@ ); MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.proxy.pin; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; diff --git a/macos/Runner/RunnerProfile.entitlements b/macos/Runner/RunnerProfile.entitlements index e7e2409..9d11681 100644 --- a/macos/Runner/RunnerProfile.entitlements +++ b/macos/Runner/RunnerProfile.entitlements @@ -3,7 +3,7 @@ com.apple.security.app-sandbox - + com.apple.security.cs.allow-jit com.apple.security.files.user-selected.read-write diff --git a/pubspec.yaml b/pubspec.yaml index 2a4222f..a248490 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: network_proxy description: ProxyPin publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 1.0.5+5 +version: 1.0.6+0 environment: sdk: '>=3.0.2 <4.0.0'