mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-03-15 04:23:17 +08:00
手机版启动默认不再自动开启抓包
This commit is contained in:
@@ -37,6 +37,14 @@
|
||||
<action android:name="andorid.net.VpnService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<activity
|
||||
android:name=".VpnAlertDialog"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="ProxyVpnService" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.os.Build
|
||||
import android.os.ParcelFileDescriptor
|
||||
|
||||
class ProxyVpnService : VpnService() {
|
||||
private lateinit var vpnInterface: ParcelFileDescriptor
|
||||
private var vpnInterface: ParcelFileDescriptor? = null
|
||||
|
||||
companion object {
|
||||
const val ProxyHost = "ProxyHost"
|
||||
@@ -35,14 +35,19 @@ class ProxyVpnService : VpnService() {
|
||||
}
|
||||
|
||||
private fun disconnect() {
|
||||
vpnInterface.close()
|
||||
vpnInterface?.close()
|
||||
}
|
||||
|
||||
private fun connect(proxyHost: String, proxyPort: Int) {
|
||||
vpnInterface = createVpnInterface(proxyHost, proxyPort)
|
||||
if (vpnInterface == null) {
|
||||
val alertDialog = Intent(applicationContext, VpnAlertDialog::class.java)
|
||||
alertDialog.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivity(alertDialog)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createVpnInterface(proxyHost: String, proxyPort: Int): ParcelFileDescriptor {
|
||||
private fun createVpnInterface(proxyHost: String, proxyPort: Int): ParcelFileDescriptor? {
|
||||
return Builder()
|
||||
.addAddress("10.0.0.2", 32)
|
||||
.addRoute("0.0.0.0", 0)
|
||||
@@ -53,7 +58,7 @@ class ProxyVpnService : VpnService() {
|
||||
.setHttpProxy(ProxyInfo.buildDirectProxy(proxyHost, proxyPort))
|
||||
}
|
||||
}
|
||||
.establish() ?: throw IllegalStateException("无法初始化vpnInterface")
|
||||
.establish()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.network.proxy
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.os.Bundle
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
||||
class VpnAlertDialog : Activity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val dialog: AlertDialog = AlertDialog.Builder(this)
|
||||
.setTitle("提示")
|
||||
.setMessage("必须添加VPN才能使用")
|
||||
.setPositiveButton("确认") { _, _ ->
|
||||
exitProcess(0)
|
||||
}
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,13 +10,6 @@ import NetworkExtension
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
|
||||
// let url = URL(string: "http://www.baidu.com")!
|
||||
// let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
|
||||
// guard let data = data else { return }
|
||||
// print(String(data: data, encoding: .utf8)!)
|
||||
// }
|
||||
// task.resume()
|
||||
|
||||
let controller: FlutterViewController = window.rootViewController as! FlutterViewController ;
|
||||
let batteryChannel = FlutterMethodChannel.init(name: "com.proxy/proxyVpn", binaryMessenger: controller as! FlutterBinaryMessenger);
|
||||
batteryChannel.setMethodCallHandler({
|
||||
@@ -54,14 +47,11 @@ import NetworkExtension
|
||||
}
|
||||
}
|
||||
|
||||
var backgroundUpdateTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier(rawValue: 0)
|
||||
func endBackgroundUpdateTask() {
|
||||
AudioManager.shared.openBackgroundAudioAutoplay = false
|
||||
UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
|
||||
self.backgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
|
||||
}
|
||||
|
||||
override func applicationWillResignActive(_ application: UIApplication) {
|
||||
if (!VpnManager.shared.isRunning()) {
|
||||
return
|
||||
}
|
||||
|
||||
AudioManager.shared.openBackgroundAudioAutoplay = true
|
||||
self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
|
||||
self.endBackgroundUpdateTask()
|
||||
@@ -69,7 +59,17 @@ import NetworkExtension
|
||||
}
|
||||
override func applicationDidBecomeActive(_ application: UIApplication) {
|
||||
self.endBackgroundUpdateTask()
|
||||
|
||||
}
|
||||
|
||||
var backgroundUpdateTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier(rawValue: 0)
|
||||
func endBackgroundUpdateTask() {
|
||||
if (!VpnManager.shared.isRunning()) {
|
||||
return
|
||||
}
|
||||
|
||||
AudioManager.shared.openBackgroundAudioAutoplay = false
|
||||
UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
|
||||
self.backgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -169,5 +169,9 @@ extension VpnManager{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func isRunning() -> Bool {
|
||||
return vpnStatus == VPNStatus.on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'dart:io';
|
||||
import 'package:chinese_font_library/chinese_font_library.dart';
|
||||
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/ui/component/split_view.dart';
|
||||
import 'package:network_proxy/ui/content/body.dart';
|
||||
@@ -20,11 +21,6 @@ import 'network/handler.dart';
|
||||
import 'network/http/http.dart';
|
||||
|
||||
void main(List<String> args) async {
|
||||
if (Platforms.isMobile()) {
|
||||
runApp(const FluentApp(MobileHomePage()));
|
||||
return;
|
||||
}
|
||||
|
||||
//多窗口
|
||||
if (args.firstOrNull == 'multi_window') {
|
||||
final windowId = int.parse(args[1]);
|
||||
@@ -34,6 +30,13 @@ void main(List<String> args) async {
|
||||
}
|
||||
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
var configuration = Configuration.instance;
|
||||
if (Platforms.isMobile()) {
|
||||
runApp(FluentApp(MobileHomePage(configuration: (await configuration))));
|
||||
return;
|
||||
}
|
||||
|
||||
await windowManager.ensureInitialized();
|
||||
//设置窗口大小
|
||||
WindowOptions windowOptions = WindowOptions(
|
||||
@@ -46,7 +49,7 @@ void main(List<String> args) async {
|
||||
await windowManager.focus();
|
||||
});
|
||||
|
||||
runApp(const FluentApp(DesktopHomePage()));
|
||||
runApp(FluentApp(DesktopHomePage(configuration: (await configuration))));
|
||||
}
|
||||
|
||||
///多窗口
|
||||
@@ -104,7 +107,9 @@ class FluentApp extends StatelessWidget {
|
||||
}
|
||||
|
||||
class DesktopHomePage extends StatefulWidget {
|
||||
const DesktopHomePage({super.key});
|
||||
final Configuration configuration;
|
||||
|
||||
const DesktopHomePage({super.key, required this.configuration});
|
||||
|
||||
@override
|
||||
State<DesktopHomePage> createState() => _DesktopHomePagePageState();
|
||||
@@ -129,13 +134,10 @@ class _DesktopHomePagePageState extends State<DesktopHomePage> implements EventL
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
proxyServer = ProxyServer(listener: this);
|
||||
proxyServer = ProxyServer(widget.configuration, listener: this);
|
||||
panel = NetworkTabController(tabStyle: const TextStyle(fontSize: 18), proxyServer: proxyServer);
|
||||
|
||||
proxyServer.initializedListener(() {
|
||||
if (!proxyServer.guide) {
|
||||
return;
|
||||
}
|
||||
if (widget.configuration.guide) {
|
||||
//首次引导
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -145,8 +147,8 @@ class _DesktopHomePagePageState extends State<DesktopHomePage> implements EventL
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
proxyServer.guide = false;
|
||||
proxyServer.flushConfig();
|
||||
widget.configuration.guide = false;
|
||||
widget.configuration.flushConfig();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('关闭'))
|
||||
@@ -155,7 +157,9 @@ class _DesktopHomePagePageState extends State<DesktopHomePage> implements EventL
|
||||
content: const Text('默认不会开启HTTPS抓包,请安装证书后再开启HTTPS抓包。\n'
|
||||
'点击的HTTPS抓包(加锁图标),选择安装根证书,按照提示操作即可。'));
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
142
lib/network/bin/configuration.dart
Normal file
142
lib/network/bin/configuration.dart
Normal file
@@ -0,0 +1,142 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:network_proxy/network/util/host_filter.dart';
|
||||
import 'package:network_proxy/network/util/logger.dart';
|
||||
import 'package:network_proxy/network/util/request_rewrite.dart';
|
||||
import 'package:network_proxy/utils/platform.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class Configuration {
|
||||
int port = 9099;
|
||||
|
||||
//是否启用https抓包
|
||||
bool enableSsl = false;
|
||||
|
||||
//是否启用桌面抓包
|
||||
bool enableDesktop = true;
|
||||
|
||||
//是否引导
|
||||
bool guide = false;
|
||||
|
||||
//是否显示更新内容公告
|
||||
bool upgradeNotice = true;
|
||||
|
||||
//请求重写
|
||||
RequestRewrites requestRewrites = RequestRewrites();
|
||||
|
||||
Configuration._();
|
||||
|
||||
/// 单例
|
||||
static Configuration? _instance;
|
||||
|
||||
static Future<Configuration> get instance async {
|
||||
if (_instance == null) {
|
||||
Configuration configuration = Configuration._();
|
||||
await configuration.initConfig();
|
||||
_instance = configuration;
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
/// 初始化配置
|
||||
Future<void> initConfig() async {
|
||||
// 读取配置文件
|
||||
await _loadConfig();
|
||||
}
|
||||
|
||||
Future<File> homeDir() async {
|
||||
String? userHome;
|
||||
if (Platforms.isDesktop()) {
|
||||
userHome = Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
|
||||
} else {
|
||||
userHome = (await getApplicationSupportDirectory()).path;
|
||||
}
|
||||
|
||||
var separator = Platform.pathSeparator;
|
||||
return File("${userHome!}$separator.proxypin");
|
||||
}
|
||||
|
||||
/// 配置文件
|
||||
Future<File> configFile() async {
|
||||
var separator = Platform.pathSeparator;
|
||||
var home = await homeDir();
|
||||
return File("${home.path}${separator}config.cnf");
|
||||
}
|
||||
|
||||
/// 刷新配置文件
|
||||
flushConfig() async {
|
||||
var file = await configFile();
|
||||
var exists = await file.exists();
|
||||
if (!exists) {
|
||||
file = await file.create(recursive: true);
|
||||
}
|
||||
HostFilter.whitelist.toJson();
|
||||
HostFilter.blacklist.toJson();
|
||||
var json = jsonEncode(toJson());
|
||||
logger.i('刷新配置文件 $runtimeType ${toJson()}');
|
||||
file.writeAsString(json);
|
||||
}
|
||||
|
||||
/// 加载配置文件
|
||||
Future<void> _loadConfig() async {
|
||||
var file = await configFile();
|
||||
var exits = await file.exists();
|
||||
if (!exits) {
|
||||
guide = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, dynamic> config = jsonDecode(await file.readAsString());
|
||||
logger.i('加载配置文件 [$file]');
|
||||
port = config['port'] ?? port;
|
||||
enableSsl = config['enableSsl'] == true;
|
||||
enableDesktop = config['enableDesktop'] ?? true;
|
||||
guide = config['guide'] ?? false;
|
||||
upgradeNotice = config['upgradeNotice'] ?? true;
|
||||
HostFilter.whitelist.load(config['whitelist']);
|
||||
HostFilter.blacklist.load(config['blacklist']);
|
||||
|
||||
await _loadRequestRewriteConfig();
|
||||
}
|
||||
|
||||
/// 加载请求重写配置文件
|
||||
Future<void> _loadRequestRewriteConfig() async {
|
||||
var home = await homeDir();
|
||||
var file = File('${home.path}${Platform.pathSeparator}request_rewrite.json');
|
||||
var exits = await file.exists();
|
||||
if (!exits) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, dynamic> config = jsonDecode(await file.readAsString());
|
||||
|
||||
logger.i('加载请求重写配置文件 [$file]');
|
||||
requestRewrites.load(config);
|
||||
}
|
||||
|
||||
/// 保存请求重写配置文件
|
||||
flushRequestRewriteConfig() async {
|
||||
var home = await homeDir();
|
||||
var file = File('${home.path}${Platform.pathSeparator}request_rewrite.json');
|
||||
bool exists = await file.exists();
|
||||
if (!exists) {
|
||||
await file.create(recursive: true);
|
||||
}
|
||||
var json = jsonEncode(requestRewrites.toJson());
|
||||
logger.i('刷新请求重写配置文件 ${file.path}');
|
||||
file.writeAsString(json);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'guide': guide,
|
||||
'upgradeNotice': upgradeNotice,
|
||||
'port': port,
|
||||
'enableSsl': enableSsl,
|
||||
'enableDesktop': enableDesktop,
|
||||
'whitelist': HostFilter.whitelist.toJson(),
|
||||
'blacklist': HostFilter.blacklist.toJson(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,82 +1,39 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:network_proxy/network/util/host_filter.dart';
|
||||
import 'package:network_proxy/utils/platform.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
|
||||
import '../channel.dart';
|
||||
import '../handler.dart';
|
||||
import '../http/codec.dart';
|
||||
import '../util/logger.dart';
|
||||
import '../util/request_rewrite.dart';
|
||||
import '../util/system_proxy.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
ProxyServer().start();
|
||||
var configuration = await Configuration.instance;
|
||||
ProxyServer(configuration).start();
|
||||
}
|
||||
|
||||
/// 代理服务器
|
||||
class ProxyServer {
|
||||
//是否初始化
|
||||
bool init = false;
|
||||
int port = 9099;
|
||||
|
||||
//是否启用https抓包
|
||||
bool _enableSsl = false;
|
||||
|
||||
//是否启用桌面抓包
|
||||
bool enableDesktop = true;
|
||||
|
||||
//是否引导
|
||||
bool guide = false;
|
||||
|
||||
//是否启动
|
||||
bool get isRunning => server?.isRunning ?? false;
|
||||
|
||||
Server? server;
|
||||
|
||||
//请求事件监听
|
||||
EventListener? listener;
|
||||
|
||||
//请求重写
|
||||
RequestRewrites requestRewrites = RequestRewrites();
|
||||
final Configuration configuration;
|
||||
|
||||
final List<Function> _initializedListeners = [];
|
||||
|
||||
ProxyServer({this.listener});
|
||||
|
||||
//初始化
|
||||
Future<void> initializedListener(Function action) async {
|
||||
_initializedListeners.add(action);
|
||||
}
|
||||
|
||||
Future<File> homeDir() async {
|
||||
String? userHome;
|
||||
if (Platforms.isDesktop()) {
|
||||
userHome =
|
||||
Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
|
||||
} else {
|
||||
userHome = (await getApplicationSupportDirectory()).path;
|
||||
}
|
||||
|
||||
var separator = Platform.pathSeparator;
|
||||
return File("${userHome!}$separator.proxypin");
|
||||
}
|
||||
|
||||
/// 配置文件
|
||||
Future<File> configFile() async {
|
||||
var separator = Platform.pathSeparator;
|
||||
var home = await homeDir();
|
||||
return File("${home.path}${separator}config.cnf");
|
||||
}
|
||||
ProxyServer(this.configuration, {this.listener});
|
||||
|
||||
///是否启用https抓包
|
||||
bool get enableSsl => _enableSsl;
|
||||
bool get enableSsl => configuration.enableSsl;
|
||||
|
||||
int get port => configuration.port;
|
||||
|
||||
set enableSsl(bool enableSsl) {
|
||||
_enableSsl = enableSsl;
|
||||
configuration.enableSsl = enableSsl;
|
||||
server?.enableSsl = enableSsl;
|
||||
if (server == null || server?.isRunning == false) {
|
||||
return;
|
||||
@@ -90,27 +47,16 @@ class ProxyServer {
|
||||
/// 启动代理服务
|
||||
Future<Server> start() async {
|
||||
Server server = Server();
|
||||
if (!init) {
|
||||
// 读取配置文件
|
||||
init = true;
|
||||
await _loadConfig();
|
||||
for (var element in _initializedListeners) {
|
||||
element.call();
|
||||
}
|
||||
}
|
||||
|
||||
server.enableSsl = _enableSsl;
|
||||
server.enableSsl = configuration.enableSsl;
|
||||
server.initChannel((channel) {
|
||||
channel.pipeline.handle(
|
||||
HttpRequestCodec(),
|
||||
HttpResponseCodec(),
|
||||
HttpChannelHandler(
|
||||
listener: listener, requestRewrites: requestRewrites));
|
||||
channel.pipeline.handle(HttpRequestCodec(), HttpResponseCodec(),
|
||||
HttpChannelHandler(listener: listener, requestRewrites: configuration.requestRewrites));
|
||||
});
|
||||
return server.bind(port).then((serverSocket) {
|
||||
logger.i("listen on $port");
|
||||
this.server = server;
|
||||
if (enableDesktop) {
|
||||
if (configuration.enableDesktop) {
|
||||
SystemProxy.setSystemProxy(port, enableSsl);
|
||||
}
|
||||
return server;
|
||||
@@ -120,7 +66,7 @@ class ProxyServer {
|
||||
/// 停止代理服务
|
||||
Future<Server?> stop() async {
|
||||
logger.i("stop on $port");
|
||||
if (enableDesktop) {
|
||||
if (configuration.enableDesktop) {
|
||||
if (Platform.isMacOS) {
|
||||
await SystemProxy.setProxyEnableMacOS(false, enableSsl);
|
||||
} else if (Platform.isWindows) {
|
||||
@@ -135,80 +81,4 @@ class ProxyServer {
|
||||
restart() {
|
||||
stop().then((value) => start());
|
||||
}
|
||||
|
||||
/// 刷新配置文件
|
||||
flushConfig() async {
|
||||
var file = await configFile();
|
||||
var exists = await file.exists();
|
||||
if (!exists) {
|
||||
file = await file.create(recursive: true);
|
||||
}
|
||||
HostFilter.whitelist.toJson();
|
||||
HostFilter.blacklist.toJson();
|
||||
var json = jsonEncode(toJson());
|
||||
logger.i('刷新配置文件 $runtimeType ${toJson()}');
|
||||
file.writeAsString(json);
|
||||
}
|
||||
|
||||
/// 加载配置文件
|
||||
Future<void> _loadConfig() async {
|
||||
var file = await configFile();
|
||||
var exits = await file.exists();
|
||||
if (!exits) {
|
||||
guide = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, dynamic> config = jsonDecode(await file.readAsString());
|
||||
logger.i('加载配置文件 [$file]');
|
||||
port = config['port'] ?? port;
|
||||
enableSsl = config['enableSsl'] == true;
|
||||
enableDesktop = config['enableDesktop'] ?? true;
|
||||
guide = config['guide'] ?? false;
|
||||
HostFilter.whitelist.load(config['whitelist']);
|
||||
HostFilter.blacklist.load(config['blacklist']);
|
||||
|
||||
await _loadRequestRewriteConfig();
|
||||
}
|
||||
|
||||
/// 加载请求重写配置文件
|
||||
Future<void> _loadRequestRewriteConfig() async {
|
||||
var home = await homeDir();
|
||||
var file =
|
||||
File('${home.path}${Platform.pathSeparator}request_rewrite.json');
|
||||
var exits = await file.exists();
|
||||
if (!exits) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, dynamic> config = jsonDecode(await file.readAsString());
|
||||
|
||||
logger.i('加载请求重写配置文件 [$file]');
|
||||
requestRewrites.load(config);
|
||||
}
|
||||
|
||||
/// 保存请求重写配置文件
|
||||
flushRequestRewriteConfig() async {
|
||||
var home = await homeDir();
|
||||
var file =
|
||||
File('${home.path}${Platform.pathSeparator}request_rewrite.json');
|
||||
bool exists = await file.exists();
|
||||
if (!exists) {
|
||||
await file.create(recursive: true);
|
||||
}
|
||||
var json = jsonEncode(requestRewrites.toJson());
|
||||
logger.i('刷新请求重写配置文件 ${file.path}');
|
||||
file.writeAsString(json);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'guide': guide,
|
||||
'port': port,
|
||||
'enableSsl': enableSsl,
|
||||
'enableDesktop': enableDesktop,
|
||||
'whitelist': HostFilter.whitelist.toJson(),
|
||||
'blacklist': HostFilter.blacklist.toJson(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ class HttpBodyState extends State<HttpBodyWidget> {
|
||||
var size = MediaQuery.of(context).size;
|
||||
var ratio = 1.0;
|
||||
if (Platform.isWindows) {
|
||||
WindowManager.instance.getDevicePixelRatio();
|
||||
ratio = WindowManager.instance.getDevicePixelRatio();
|
||||
}
|
||||
final window = await DesktopMultiWindow.createWindow(jsonEncode(
|
||||
{'name': 'HttpBodyWidget', 'httpMessage': widget.httpMessage, 'inNewWindow': true},
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:collection';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/channel.dart';
|
||||
import 'package:network_proxy/network/http/http.dart';
|
||||
@@ -154,7 +155,15 @@ class HeaderBody extends StatefulWidget {
|
||||
|
||||
///根据文本过滤
|
||||
Iterable<PathRow> filter(String text) {
|
||||
return _body.where((element) => element.request.requestUrl.toLowerCase().contains(text));
|
||||
return _body.where((element) {
|
||||
if (element.request.method.name.toLowerCase() == text) {
|
||||
return true;
|
||||
}
|
||||
if (element.request.requestUrl.toLowerCase().contains(text)) {
|
||||
return true;
|
||||
}
|
||||
return element.response.get()?.contentType.name.toLowerCase().contains(text) == true;
|
||||
});
|
||||
}
|
||||
|
||||
///复制
|
||||
@@ -175,12 +184,13 @@ class HeaderBody extends StatefulWidget {
|
||||
|
||||
class _HeaderBodyState extends State<HeaderBody> {
|
||||
final GlobalKey<ColorTransitionState> transitionState = GlobalKey<ColorTransitionState>();
|
||||
|
||||
late Configuration configuration;
|
||||
late bool selected;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
configuration = widget.proxyServer.configuration;
|
||||
selected = widget.selected;
|
||||
}
|
||||
|
||||
@@ -241,21 +251,21 @@ class _HeaderBodyState extends State<HeaderBody> {
|
||||
child: const Text("添加黑名单", style: TextStyle(fontSize: 14)),
|
||||
onTap: () {
|
||||
HostFilter.blacklist.add(widget.header.host);
|
||||
widget.proxyServer.flushConfig();
|
||||
configuration.flushConfig();
|
||||
}),
|
||||
PopupMenuItem(
|
||||
height: 38,
|
||||
child: const Text("添加白名单", style: TextStyle(fontSize: 14)),
|
||||
onTap: () {
|
||||
HostFilter.whitelist.add(widget.header.host);
|
||||
widget.proxyServer.flushConfig();
|
||||
configuration.flushConfig();
|
||||
}),
|
||||
PopupMenuItem(
|
||||
height: 38,
|
||||
child: const Text("删除白名单", style: TextStyle(fontSize: 14)),
|
||||
onTap: () {
|
||||
HostFilter.whitelist.remove(widget.header.host);
|
||||
widget.proxyServer.flushConfig();
|
||||
configuration.flushConfig();
|
||||
}),
|
||||
PopupMenuItem(height: 38, child: const Text("删除", style: TextStyle(fontSize: 14)), onTap: () => _delete()),
|
||||
],
|
||||
|
||||
@@ -131,7 +131,7 @@ class _PathRowState extends State<PathRow> {
|
||||
var size = MediaQuery.of(context).size;
|
||||
var ratio = 1.0;
|
||||
if (Platform.isWindows) {
|
||||
WindowManager.instance.getDevicePixelRatio();
|
||||
ratio = WindowManager.instance.getDevicePixelRatio();
|
||||
}
|
||||
|
||||
final window = await DesktopMultiWindow.createWindow(jsonEncode(
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
import 'package:network_proxy/network/util/host_filter.dart';
|
||||
|
||||
|
||||
class FilterDialog extends StatefulWidget {
|
||||
final ProxyServer proxyServer;
|
||||
final Configuration configuration;
|
||||
|
||||
const FilterDialog({super.key, required this.proxyServer});
|
||||
const FilterDialog({super.key, required this.configuration});
|
||||
|
||||
@override
|
||||
State<FilterDialog> createState() => _FilterDialogState();
|
||||
@@ -49,7 +48,7 @@ class _FilterDialogState extends State<FilterDialog> {
|
||||
title: "白名单",
|
||||
subtitle: "只代理白名单中的域名, 白名单启用黑名单将会失效",
|
||||
hostList: HostFilter.whitelist,
|
||||
proxyServer: widget.proxyServer,
|
||||
configuration: widget.configuration,
|
||||
hostEnableNotifier: hostEnableNotifier)),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
@@ -58,7 +57,7 @@ class _FilterDialogState extends State<FilterDialog> {
|
||||
title: "黑名单",
|
||||
subtitle: "黑名单中的域名不会代理",
|
||||
hostList: HostFilter.blacklist,
|
||||
proxyServer: widget.proxyServer,
|
||||
configuration: widget.configuration,
|
||||
hostEnableNotifier: hostEnableNotifier)),
|
||||
],
|
||||
),
|
||||
@@ -70,7 +69,7 @@ class DomainFilter extends StatefulWidget {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final HostList hostList;
|
||||
final ProxyServer proxyServer;
|
||||
final Configuration configuration;
|
||||
final ValueNotifier<bool> hostEnableNotifier;
|
||||
|
||||
const DomainFilter(
|
||||
@@ -79,7 +78,7 @@ class DomainFilter extends StatefulWidget {
|
||||
required this.subtitle,
|
||||
required this.hostList,
|
||||
required this.hostEnableNotifier,
|
||||
required this.proxyServer});
|
||||
required this.configuration});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
@@ -145,7 +144,7 @@ class _DomainFilterState extends State<DomainFilter> {
|
||||
@override
|
||||
void dispose() {
|
||||
if (changed) {
|
||||
widget.proxyServer.flushConfig();
|
||||
widget.configuration.flushConfig();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
import 'package:network_proxy/network/util/request_rewrite.dart';
|
||||
|
||||
class RequestRewrite extends StatefulWidget {
|
||||
final ProxyServer proxyServer;
|
||||
final Configuration configuration;
|
||||
|
||||
const RequestRewrite({super.key, required this.proxyServer});
|
||||
const RequestRewrite({super.key, required this.configuration});
|
||||
|
||||
@override
|
||||
State<RequestRewrite> createState() => _RequestRewriteState();
|
||||
@@ -19,15 +19,15 @@ class _RequestRewriteState extends State<RequestRewrite> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
requestRuleList = RequestRuleList(widget.proxyServer.requestRewrites);
|
||||
enableNotifier = ValueNotifier(widget.proxyServer.requestRewrites.enabled == true);
|
||||
requestRuleList = RequestRuleList(widget.configuration.requestRewrites);
|
||||
enableNotifier = ValueNotifier(widget.configuration.requestRewrites.enabled == true);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (changed || enableNotifier.value != widget.proxyServer.requestRewrites.enabled) {
|
||||
widget.proxyServer.requestRewrites.enabled = enableNotifier.value;
|
||||
widget.proxyServer.flushRequestRewriteConfig();
|
||||
if (changed || enableNotifier.value != widget.configuration.requestRewrites.enabled) {
|
||||
widget.configuration.requestRewrites.enabled = enableNotifier.value;
|
||||
widget.configuration.flushRequestRewriteConfig();
|
||||
}
|
||||
|
||||
enableNotifier.dispose();
|
||||
@@ -80,7 +80,7 @@ class _RequestRewriteState extends State<RequestRewrite> {
|
||||
|
||||
changed = true;
|
||||
setState(() {
|
||||
widget.proxyServer.requestRewrites.removeIndex(removeSelected);
|
||||
widget.configuration.requestRewrites.removeIndex(removeSelected);
|
||||
requestRuleList.changeState();
|
||||
});
|
||||
})
|
||||
@@ -97,7 +97,7 @@ class _RequestRewriteState extends State<RequestRewrite> {
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return RuleAddDialog(
|
||||
requestRewrites: widget.proxyServer.requestRewrites,
|
||||
requestRewrites: widget.configuration.requestRewrites,
|
||||
currentIndex: currentIndex,
|
||||
onChange: () {
|
||||
changed = true;
|
||||
@@ -250,7 +250,7 @@ class _RequestRuleListState extends State<RequestRuleList> {
|
||||
border: TableBorder.symmetric(outside: BorderSide(width: 1, color: Theme.of(context).highlightColor)),
|
||||
columns: const <DataColumn>[
|
||||
DataColumn(label: Text('启用')),
|
||||
DataColumn(label: Text('URL')),
|
||||
DataColumn(label: Text('Path')),
|
||||
DataColumn(label: Text('请求体')),
|
||||
DataColumn(label: Text('响应体')),
|
||||
],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/util/system_proxy.dart';
|
||||
import 'package:network_proxy/ui/desktop/toolbar/setting/request_rewrite.dart';
|
||||
@@ -20,10 +21,12 @@ class Setting extends StatefulWidget {
|
||||
|
||||
class _SettingState extends State<Setting> {
|
||||
late ValueNotifier<bool> enableDesktopListenable;
|
||||
late Configuration configuration;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
enableDesktopListenable = ValueNotifier<bool>(widget.proxyServer.enableDesktop);
|
||||
configuration = widget.proxyServer.configuration;
|
||||
enableDesktopListenable = ValueNotifier<bool>(configuration.enableDesktop);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@@ -54,12 +57,12 @@ class _SettingState extends State<Setting> {
|
||||
title: const Text("抓取电脑请求"),
|
||||
visualDensity: const VisualDensity(horizontal: -4),
|
||||
dense: true,
|
||||
value: widget.proxyServer.enableDesktop,
|
||||
value: configuration.enableDesktop,
|
||||
onChanged: (val) {
|
||||
SystemProxy.setSystemProxyEnable(widget.proxyServer.port, val, widget.proxyServer.enableSsl);
|
||||
widget.proxyServer.enableDesktop = val;
|
||||
configuration.enableDesktop = val;
|
||||
enableDesktopListenable.value = !enableDesktopListenable.value;
|
||||
widget.proxyServer.flushConfig();
|
||||
configuration.flushConfig();
|
||||
}))),
|
||||
const PopupMenuItem(padding: EdgeInsets.all(0), child: ThemeSetting(dense: true)),
|
||||
menuItem("域名过滤", onTap: () => hostFilter()),
|
||||
@@ -106,7 +109,7 @@ class _SettingState extends State<Setting> {
|
||||
label: const Text("关闭"),
|
||||
onPressed: () => Navigator.of(context).pop())))
|
||||
]),
|
||||
content: RequestRewrite(proxyServer: widget.proxyServer),
|
||||
content: RequestRewrite(configuration: configuration),
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -117,7 +120,7 @@ class _SettingState extends State<Setting> {
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return FilterDialog(proxyServer: widget.proxyServer);
|
||||
return FilterDialog(configuration: configuration);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -146,11 +149,11 @@ class _PortState extends State<PortWidget> {
|
||||
portFocus.addListener(() async {
|
||||
//失去焦点
|
||||
if (!portFocus.hasFocus && textController.text != widget.proxyServer.port.toString()) {
|
||||
widget.proxyServer.port = int.parse(textController.text);
|
||||
widget.proxyServer.configuration.port = int.parse(textController.text);
|
||||
if (widget.proxyServer.isRunning) {
|
||||
widget.proxyServer.restart();
|
||||
}
|
||||
widget.proxyServer.flushConfig();
|
||||
widget.proxyServer.configuration.flushConfig();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,22 +19,10 @@ class SslWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SslState extends State<SslWidget> {
|
||||
bool _enableSsl = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
widget.proxyServer.initializedListener(() {
|
||||
_enableSsl = widget.proxyServer.enableSsl;
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return PopupMenuButton<String>(
|
||||
icon: Icon(Icons.https, color: _enableSsl ? null : Colors.red),
|
||||
icon: Icon(Icons.https, color: widget.proxyServer.enableSsl ? null : Colors.red),
|
||||
surfaceTintColor: Colors.white70,
|
||||
tooltip: "HTTPS代理",
|
||||
offset: const Offset(10, 30),
|
||||
@@ -42,11 +30,7 @@ class _SslState extends State<SslWidget> {
|
||||
return [
|
||||
PopupMenuItem(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: _Switch(
|
||||
proxyServer: widget.proxyServer,
|
||||
onEnableChange: (val) => setState(() {
|
||||
_enableSsl = val;
|
||||
}))),
|
||||
child: _Switch(proxyServer: widget.proxyServer, onEnableChange: (val) => setState(() {}))),
|
||||
PopupMenuItem(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ListTile(
|
||||
@@ -282,7 +266,7 @@ class _SwitchState extends State<_Switch> {
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
if (changed) {
|
||||
widget.proxyServer.flushConfig();
|
||||
widget.proxyServer.configuration.flushConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_toastr/flutter_toastr.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/channel.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
class SocketLaunch extends StatefulWidget {
|
||||
final ProxyServer proxyServer;
|
||||
final int size;
|
||||
final bool startup;
|
||||
final Function? onStart;
|
||||
final Function? onStop;
|
||||
|
||||
const SocketLaunch({super.key, required this.proxyServer, this.size = 25, this.onStart, this.onStop});
|
||||
const SocketLaunch(
|
||||
{super.key, required this.proxyServer, this.size = 25, this.onStart, this.onStop, this.startup = true});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
@@ -27,14 +28,9 @@ class _SocketLaunchState extends State<SocketLaunch> with WindowListener, Widget
|
||||
windowManager.addListener(this);
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
//启动代理服务器
|
||||
widget.proxyServer.start().then((value) {
|
||||
setState(() {
|
||||
started = true;
|
||||
});
|
||||
widget.onStart?.call();
|
||||
}).catchError((e) {
|
||||
FlutterToastr.show("启动失败,请检查端口号${widget.proxyServer.port}是否被占用", context, duration: 3);
|
||||
});
|
||||
if (widget.startup) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -70,19 +66,27 @@ class _SocketLaunchState extends State<SocketLaunch> with WindowListener, Widget
|
||||
icon: Icon(started ? Icons.stop : Icons.play_arrow_sharp,
|
||||
color: started ? Colors.red : Colors.green, size: widget.size.toDouble()),
|
||||
onPressed: () async {
|
||||
Future<Server?> result = started ? widget.proxyServer.stop() : widget.proxyServer.start();
|
||||
if (started) {
|
||||
widget.onStop?.call();
|
||||
widget.proxyServer.stop().then((value) {
|
||||
widget.onStop?.call();
|
||||
setState(() {
|
||||
started = !started;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
widget.onStart?.call();
|
||||
start();
|
||||
}
|
||||
result
|
||||
.then((value) => setState(() {
|
||||
started = !started;
|
||||
}))
|
||||
.catchError((e) {
|
||||
FlutterToastr.show("启动失败,请检查端口号${widget.proxyServer.port}是否被占用", context);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
start() {
|
||||
widget.proxyServer.start().then((value) {
|
||||
setState(() {
|
||||
started = true;
|
||||
});
|
||||
widget.onStart?.call();
|
||||
}).catchError((e) {
|
||||
FlutterToastr.show("启动失败,请检查端口号${widget.proxyServer.port}是否被占用", context, duration: 3);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/http_client.dart';
|
||||
import 'package:network_proxy/network/util/host_filter.dart';
|
||||
@@ -75,7 +76,7 @@ class ConnectRemoteState extends State<ConnectRemote> {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return ConfigSyncWidget(proxyServer: widget.proxyServer, config: config);
|
||||
return ConfigSyncWidget(configuration: widget.proxyServer.configuration, config: config);
|
||||
});
|
||||
}
|
||||
}).onError((error, stackTrace) {
|
||||
@@ -86,10 +87,10 @@ class ConnectRemoteState extends State<ConnectRemote> {
|
||||
}
|
||||
|
||||
class ConfigSyncWidget extends StatefulWidget {
|
||||
final ProxyServer proxyServer;
|
||||
final Configuration configuration;
|
||||
final Map<String, dynamic> config;
|
||||
|
||||
const ConfigSyncWidget({super.key, required this.proxyServer, required this.config});
|
||||
const ConfigSyncWidget({super.key, required this.configuration, required this.config});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
@@ -152,10 +153,10 @@ class ConfigSyncState extends State<ConfigSyncWidget> {
|
||||
HostFilter.blacklist.load(widget.config['blacklist']);
|
||||
}
|
||||
if (syncRewrite) {
|
||||
widget.proxyServer.requestRewrites.load(widget.config['requestRewrites']);
|
||||
widget.proxyServer.flushRequestRewriteConfig();
|
||||
widget.configuration.requestRewrites.load(widget.config['requestRewrites']);
|
||||
widget.configuration.flushRequestRewriteConfig();
|
||||
}
|
||||
widget.proxyServer.flushConfig();
|
||||
widget.configuration.flushConfig();
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('同步成功')));
|
||||
}),
|
||||
|
||||
@@ -43,17 +43,17 @@ class DrawerWidget extends StatelessWidget {
|
||||
ListTile(
|
||||
title: const Text("域名白名单"),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: () =>
|
||||
navigator(context, MobileFilterWidget(proxyServer: proxyServer, hostList: HostFilter.whitelist))),
|
||||
onTap: () => navigator(
|
||||
context, MobileFilterWidget(configuration: proxyServer.configuration, hostList: HostFilter.whitelist))),
|
||||
ListTile(
|
||||
title: const Text("域名黑名单"),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: () =>
|
||||
navigator(context, MobileFilterWidget(proxyServer: proxyServer, hostList: HostFilter.blacklist))),
|
||||
onTap: () => navigator(
|
||||
context, MobileFilterWidget(configuration: proxyServer.configuration, hostList: HostFilter.blacklist))),
|
||||
ListTile(
|
||||
title: const Text("请求重写"),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
onTap: () => navigator(context, MobileRequestRewrite(proxyServer: proxyServer))),
|
||||
onTap: () => navigator(context, MobileRequestRewrite(configuration: proxyServer.configuration))),
|
||||
ListTile(
|
||||
title: const Text("Github"),
|
||||
trailing: const Icon(Icons.arrow_right),
|
||||
@@ -169,7 +169,7 @@ class MoreEnum extends StatelessWidget {
|
||||
hostname: response.headers.get("hostname"));
|
||||
|
||||
if (context.mounted && Navigator.canPop(context)) {
|
||||
FlutterToastr.show("连接成功", context);
|
||||
FlutterToastr.show("连接成功${proxyServer.isRunning ? '' : ',手机需要开启抓包才可以抓取请求哦'}", context);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:network_proxy/native/vpn.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/channel.dart';
|
||||
import 'package:network_proxy/network/handler.dart';
|
||||
@@ -13,7 +14,9 @@ import 'package:network_proxy/ui/mobile/menu.dart';
|
||||
import 'package:network_proxy/ui/mobile/request/list.dart';
|
||||
|
||||
class MobileHomePage extends StatefulWidget {
|
||||
const MobileHomePage({super.key});
|
||||
final Configuration configuration;
|
||||
|
||||
const MobileHomePage({super.key, required this.configuration});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
@@ -39,7 +42,7 @@ class MobileHomeState extends State<MobileHomePage> implements EventListener {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
proxyServer = ProxyServer(listener: this);
|
||||
proxyServer = ProxyServer(widget.configuration, listener: this);
|
||||
desktop.addListener(() {
|
||||
if (desktop.value.connect) {
|
||||
proxyServer.server?.remoteHost = "http://${desktop.value.host}:${desktop.value.port}";
|
||||
@@ -48,7 +51,26 @@ class MobileHomeState extends State<MobileHomePage> implements EventListener {
|
||||
proxyServer.server?.remoteHost = null;
|
||||
}
|
||||
});
|
||||
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (widget.configuration.guide) {
|
||||
//首次引导
|
||||
String content = '默认不会开启HTTPS抓包,请安装证书后再开启HTTPS抓包。\n'
|
||||
'点击的设置 -> HTTPS抓包,根据提示安装证书操作即可。';
|
||||
showAlertDialog('提示', content, () {
|
||||
widget.configuration.guide = false;
|
||||
widget.configuration.upgradeNotice = false;
|
||||
widget.configuration.flushConfig();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (widget.configuration.upgradeNotice) {
|
||||
showUpgradeNotice();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -74,6 +96,7 @@ class MobileHomeState extends State<MobileHomePage> implements EventListener {
|
||||
onPressed: () {},
|
||||
child: SocketLaunch(
|
||||
proxyServer: proxyServer,
|
||||
startup: false,
|
||||
size: 38,
|
||||
onStart: () => Vpn.startVpn("127.0.0.1", proxyServer.port),
|
||||
onStop: () => Vpn.stopVpn())),
|
||||
@@ -100,6 +123,32 @@ class MobileHomeState extends State<MobileHomePage> implements EventListener {
|
||||
);
|
||||
}
|
||||
|
||||
showUpgradeNotice() {
|
||||
String content = '1. 手机版启动默认不再自动开启抓包,请手动点击启动按钮。\n'
|
||||
'2. 增加外部代理,可配置其他VPN软件地址,开启抓包不会影响访问外网。\n'
|
||||
'3. 搜索功能增强,可直接搜索响应类型和请求方法。';
|
||||
showAlertDialog('更新内容', content, () {
|
||||
widget.configuration.upgradeNotice = false;
|
||||
widget.configuration.flushConfig();
|
||||
});
|
||||
}
|
||||
|
||||
showAlertDialog(String title, String content, Function onClose) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) {
|
||||
return AlertDialog(actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
onClose.call();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('关闭'))
|
||||
], title: Text(title, style: const TextStyle(fontSize: 18)), content: Text(content));
|
||||
});
|
||||
}
|
||||
|
||||
/// 搜索框
|
||||
Widget search() {
|
||||
return Padding(
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'dart:collection';
|
||||
import 'package:date_format/date_format.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_toastr/flutter_toastr.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/channel.dart';
|
||||
import 'package:network_proxy/network/http/http.dart';
|
||||
@@ -160,17 +161,26 @@ class RequestSequenceState extends State<RequestSequence> with AutomaticKeepAliv
|
||||
}
|
||||
|
||||
bool filter(HttpRequest request) {
|
||||
if (searchText?.isNotEmpty == true) {
|
||||
return request.requestUrl.toLowerCase().contains(searchText!);
|
||||
if (searchText == null || searchText!.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
if (request.method.name.toLowerCase() == searchText) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (request.requestUrl.toLowerCase().contains(searchText!)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return request.response?.contentType.name.toLowerCase().contains(searchText!) == true;
|
||||
}
|
||||
|
||||
changeState() {
|
||||
//防止频繁刷新
|
||||
if (!changing) {
|
||||
changing = true;
|
||||
Future.delayed(const Duration(milliseconds: 300), () {
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
setState(() {
|
||||
changing = false;
|
||||
});
|
||||
@@ -217,6 +227,7 @@ class DomainList extends StatefulWidget {
|
||||
|
||||
class DomainListState extends State<DomainList> with AutomaticKeepAliveClientMixin {
|
||||
GlobalKey<RequestSequenceState> requestSequenceKey = GlobalKey<RequestSequenceState>();
|
||||
late Configuration configuration;
|
||||
|
||||
//域名和对应请求列表的映射
|
||||
Map<HostAndPort, List<HttpRequest>> containerMap = {};
|
||||
@@ -235,6 +246,8 @@ class DomainListState extends State<DomainList> with AutomaticKeepAliveClientMix
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
configuration = widget.proxyServer.configuration;
|
||||
|
||||
for (var request in widget.list) {
|
||||
var hostAndPort = request.hostAndPort!;
|
||||
container.add(hostAndPort);
|
||||
@@ -271,7 +284,7 @@ class DomainListState extends State<DomainList> with AutomaticKeepAliveClientMix
|
||||
//防止频繁刷新
|
||||
if (!changing) {
|
||||
changing = true;
|
||||
Future.delayed(const Duration(milliseconds: 200), () {
|
||||
Future.delayed(const Duration(milliseconds: 50), () {
|
||||
setState(() {
|
||||
changing = false;
|
||||
});
|
||||
@@ -332,7 +345,7 @@ class DomainListState extends State<DomainList> with AutomaticKeepAliveClientMix
|
||||
var time =
|
||||
formatDate(containerMap[list.elementAt(index)]!.last.requestTime, [m, '/', d, ' ', HH, ':', nn, ':', ss]);
|
||||
return ListTile(
|
||||
visualDensity: const VisualDensity( vertical: -4),
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(list.elementAt(index).domain, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
subtitle: Text("最后请求时间: $time, 次数: ${containerMap[list.elementAt(index)]!.length}",
|
||||
@@ -366,7 +379,7 @@ class DomainListState extends State<DomainList> with AutomaticKeepAliveClientMix
|
||||
child: const SizedBox(width: double.infinity, child: Text("添加黑名单", textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
HostFilter.blacklist.add(hostAndPort.host);
|
||||
widget.proxyServer.flushConfig();
|
||||
configuration.flushConfig();
|
||||
FlutterToastr.show("已添加至黑名单", context);
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
@@ -375,7 +388,7 @@ class DomainListState extends State<DomainList> with AutomaticKeepAliveClientMix
|
||||
child: const SizedBox(width: double.infinity, child: Text("添加白名单", textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
HostFilter.whitelist.add(hostAndPort.host);
|
||||
widget.proxyServer.flushConfig();
|
||||
configuration.flushConfig();
|
||||
FlutterToastr.show("已添加至白名单", context);
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
@@ -384,7 +397,7 @@ class DomainListState extends State<DomainList> with AutomaticKeepAliveClientMix
|
||||
child: const SizedBox(width: double.infinity, child: Text("删除白名单", textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
HostFilter.whitelist.remove(hostAndPort.host);
|
||||
widget.proxyServer.flushConfig();
|
||||
configuration.flushConfig();
|
||||
FlutterToastr.show("已删除白名单", context);
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
|
||||
import '../../../../network/util/host_filter.dart';
|
||||
|
||||
class MobileFilterWidget extends StatefulWidget {
|
||||
final ProxyServer proxyServer;
|
||||
final Configuration configuration;
|
||||
final HostList hostList;
|
||||
|
||||
const MobileFilterWidget({super.key, required this.proxyServer, required this.hostList});
|
||||
const MobileFilterWidget({super.key, required this.configuration, required this.hostList});
|
||||
|
||||
@override
|
||||
State<MobileFilterWidget> createState() => _MobileFilterState();
|
||||
@@ -34,7 +34,7 @@ class _MobileFilterState extends State<MobileFilterWidget> {
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
hostList: widget.hostList,
|
||||
proxyServer: widget.proxyServer,
|
||||
configuration: widget.configuration,
|
||||
hostEnableNotifier: hostEnableNotifier),
|
||||
));
|
||||
}
|
||||
@@ -44,7 +44,7 @@ class DomainFilter extends StatefulWidget {
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final HostList hostList;
|
||||
final ProxyServer proxyServer;
|
||||
final Configuration configuration;
|
||||
final ValueNotifier<bool> hostEnableNotifier;
|
||||
|
||||
const DomainFilter(
|
||||
@@ -53,7 +53,7 @@ class DomainFilter extends StatefulWidget {
|
||||
required this.subtitle,
|
||||
required this.hostList,
|
||||
required this.hostEnableNotifier,
|
||||
required this.proxyServer});
|
||||
required this.configuration});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
@@ -114,7 +114,7 @@ class _DomainFilterState extends State<DomainFilter> {
|
||||
@override
|
||||
void dispose() {
|
||||
if (changed) {
|
||||
widget.proxyServer.flushConfig();
|
||||
widget.configuration.flushConfig();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:network_proxy/network/bin/server.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
import 'package:network_proxy/network/util/request_rewrite.dart';
|
||||
|
||||
class MobileRequestRewrite extends StatefulWidget {
|
||||
final ProxyServer proxyServer;
|
||||
final Configuration configuration;
|
||||
|
||||
const MobileRequestRewrite({super.key, required this.proxyServer});
|
||||
const MobileRequestRewrite({super.key, required this.configuration});
|
||||
|
||||
@override
|
||||
State<MobileRequestRewrite> createState() => _MobileRequestRewriteState();
|
||||
@@ -19,15 +19,15 @@ class _MobileRequestRewriteState extends State<MobileRequestRewrite> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
requestRuleList = RequestRuleList(widget.proxyServer.requestRewrites);
|
||||
enableNotifier = ValueNotifier(widget.proxyServer.requestRewrites.enabled);
|
||||
requestRuleList = RequestRuleList(widget.configuration.requestRewrites);
|
||||
enableNotifier = ValueNotifier(widget.configuration.requestRewrites.enabled);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (changed || enableNotifier.value != widget.proxyServer.requestRewrites.enabled) {
|
||||
widget.proxyServer.requestRewrites.enabled = enableNotifier.value;
|
||||
widget.proxyServer.flushRequestRewriteConfig();
|
||||
if (changed || enableNotifier.value != widget.configuration.requestRewrites.enabled) {
|
||||
widget.configuration.requestRewrites.enabled = enableNotifier.value;
|
||||
widget.configuration.flushRequestRewriteConfig();
|
||||
}
|
||||
|
||||
enableNotifier.dispose();
|
||||
@@ -81,7 +81,7 @@ class _MobileRequestRewriteState extends State<MobileRequestRewrite> {
|
||||
|
||||
changed = true;
|
||||
setState(() {
|
||||
widget.proxyServer.requestRewrites.removeIndex(removeSelected);
|
||||
widget.configuration.requestRewrites.removeIndex(removeSelected);
|
||||
requestRuleList.changeState();
|
||||
});
|
||||
})
|
||||
@@ -97,7 +97,7 @@ class _MobileRequestRewriteState extends State<MobileRequestRewrite> {
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return RuleAddDialog(
|
||||
requestRewrites: widget.proxyServer.requestRewrites,
|
||||
requestRewrites: widget.configuration.requestRewrites,
|
||||
currentIndex: currentIndex,
|
||||
onChange: () {
|
||||
changed = true;
|
||||
@@ -248,7 +248,7 @@ class _RequestRuleListState extends State<RequestRuleList> {
|
||||
border: TableBorder.symmetric(outside: BorderSide(width: 1, color: Theme.of(context).highlightColor)),
|
||||
columns: const <DataColumn>[
|
||||
DataColumn(label: Text('启用')),
|
||||
DataColumn(label: Text('URL')),
|
||||
DataColumn(label: Text('Path')),
|
||||
DataColumn(label: Text('请求体')),
|
||||
DataColumn(label: Text('响应体')),
|
||||
],
|
||||
|
||||
@@ -21,7 +21,7 @@ class _MobileSslState extends State<MobileSslWidget> {
|
||||
@override
|
||||
void dispose() {
|
||||
if (changed) {
|
||||
widget.proxyServer.flushConfig();
|
||||
widget.proxyServer.configuration.flushConfig();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -9,11 +9,13 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:network_proxy/main.dart';
|
||||
import 'package:network_proxy/network/bin/configuration.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(const FluentApp(DesktopHomePage()));
|
||||
Configuration configuration = await Configuration.instance;
|
||||
await tester.pumpWidget(FluentApp(DesktopHomePage(configuration: configuration)));
|
||||
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
|
||||
Reference in New Issue
Block a user