merge: 合并 K54 iOS 网络修复
This commit is contained in:
commit
ae11846b63
3 files changed
+129
-26
No files matched your search
@@ -28,8 +28,41 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>NSAppTransportSecurity</key>
|
<key>NSAppTransportSecurity</key>
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<true/>
|
||||||
<key>NSAllowsLocalNetworking</key>
|
<key>NSAllowsLocalNetworking</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>NSExceptionDomains</key>
|
||||||
|
<dict>
|
||||||
|
<key>127.0.0.1</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSIncludesSubdomains</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>localhost</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSIncludesSubdomains</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>192.168.31.187</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSIncludesSubdomains</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>39.162.50.99</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSIncludesSubdomains</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>NSLocalNetworkUsageDescription</key>
|
<key>NSLocalNetworkUsageDescription</key>
|
||||||
<string>开发阶段需要访问同一局域网内的短剧内容服务。</string>
|
<string>开发阶段需要访问同一局域网内的短剧内容服务。</string>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// Author: fruit.com.cn
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@@ -22,12 +24,17 @@ class AppConfig {
|
|||||||
/// 公网地址(远程/移动网络访问时使用)。
|
/// 公网地址(远程/移动网络访问时使用)。
|
||||||
static const String publicBaseUrl = 'http://39.162.50.99:8081';
|
static const String publicBaseUrl = 'http://39.162.50.99:8081';
|
||||||
|
|
||||||
static const String _loopbackBaseUrl = 'http://127.0.0.1:8081';
|
|
||||||
|
|
||||||
static const String _overrideBaseUrl = String.fromEnvironment('API_BASE_URL');
|
static const String _overrideBaseUrl = String.fromEnvironment('API_BASE_URL');
|
||||||
|
|
||||||
static String _runtimeBaseUrl = '';
|
static String _runtimeBaseUrl = '';
|
||||||
|
|
||||||
|
static const Duration _probeTimeout = Duration(seconds: 2);
|
||||||
|
static const List<String> _probeCandidates = [
|
||||||
|
lanBaseUrl,
|
||||||
|
devHostBaseUrl,
|
||||||
|
publicBaseUrl,
|
||||||
|
];
|
||||||
|
|
||||||
/// Build-time server URL supplied through `--dart-define=API_BASE_URL`.
|
/// Build-time server URL supplied through `--dart-define=API_BASE_URL`.
|
||||||
static String get compiledBaseUrl => _overrideBaseUrl;
|
static String get compiledBaseUrl => _overrideBaseUrl;
|
||||||
|
|
||||||
@@ -40,7 +47,7 @@ class AppConfig {
|
|||||||
: (compiledBaseUrl.isNotEmpty ? compiledBaseUrl : defaultBaseUrl);
|
: (compiledBaseUrl.isNotEmpty ? compiledBaseUrl : defaultBaseUrl);
|
||||||
|
|
||||||
static final String _defaultBaseUrl = _runsOnDevMachine
|
static final String _defaultBaseUrl = _runsOnDevMachine
|
||||||
? _loopbackBaseUrl
|
? lanBaseUrl
|
||||||
: devHostBaseUrl;
|
: devHostBaseUrl;
|
||||||
|
|
||||||
/// 模拟器、桌面端与开发机同机,环回地址可用;iOS 真机不满足。
|
/// 模拟器、桌面端与开发机同机,环回地址可用;iOS 真机不满足。
|
||||||
@@ -66,29 +73,42 @@ class AppConfig {
|
|||||||
|
|
||||||
static const Duration requestTimeout = Duration(seconds: 8);
|
static const Duration requestTimeout = Duration(seconds: 8);
|
||||||
|
|
||||||
/// 探测内网地址是否可达,可达则切换为内网直连,否则回落到公网地址。
|
/// 按环回、开发机、公网顺序探测,写入首个返回 200 的地址。
|
||||||
///
|
///
|
||||||
/// Wi-Fi 下内网直连更快;移动数据下内网不可达,自动回落到公网端口映射。
|
/// 全部不可达时使用平台默认地址,真机保留开发机地址,避免退回公网。
|
||||||
static Future<void> probeAndApplyBaseUrl() async {
|
static Future<void> probeAndApplyBaseUrl({
|
||||||
try {
|
Future<bool> Function(String baseUrl)? probe,
|
||||||
final client = HttpClient()
|
String? fallbackBaseUrl,
|
||||||
..connectionTimeout = const Duration(seconds: 2);
|
}) async {
|
||||||
final request = await client.getUrl(
|
final candidateProbe = probe ?? _probeBaseUrl;
|
||||||
Uri.parse('$lanBaseUrl/app/categories'),
|
for (final candidate in _probeCandidates) {
|
||||||
);
|
if (await candidateProbe(candidate)) {
|
||||||
final response = await request.close().timeout(const Duration(seconds: 2));
|
applyRuntimeBaseUrl(candidate);
|
||||||
await response.drain<void>();
|
|
||||||
client.close(force: true);
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
applyRuntimeBaseUrl(lanBaseUrl);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (_) {
|
|
||||||
// 内网不可达,回落到公网。
|
|
||||||
}
|
}
|
||||||
applyRuntimeBaseUrl(
|
applyRuntimeBaseUrl(fallbackBaseUrl ?? defaultBaseUrl);
|
||||||
compiledBaseUrl.isNotEmpty ? compiledBaseUrl : publicBaseUrl,
|
}
|
||||||
);
|
|
||||||
|
static Future<bool> _probeBaseUrl(String baseUrl) async {
|
||||||
|
final client = HttpClient()..connectionTimeout = _probeTimeout;
|
||||||
|
try {
|
||||||
|
return await _requestCategories(
|
||||||
|
client,
|
||||||
|
Uri.parse('$baseUrl/app/categories'),
|
||||||
|
).timeout(_probeTimeout);
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
client.close(force: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> _requestCategories(HttpClient client, Uri uri) async {
|
||||||
|
final request = await client.getUrl(uri);
|
||||||
|
final response = await request.close();
|
||||||
|
await response.drain<void>();
|
||||||
|
return response.statusCode == 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 后端返回的是相对路径(/files/...),这里补上服务地址。
|
/// 后端返回的是相对路径(/files/...),这里补上服务地址。
|
||||||
|
|||||||
@@ -60,6 +60,59 @@ void main() {
|
|||||||
expect(AppConfig.devHostBaseUrl, 'http://192.168.31.187:8081');
|
expect(AppConfig.devHostBaseUrl, 'http://192.168.31.187:8081');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should_keep_loopback_priority_when_probe_succeeds', () async {
|
||||||
|
final attempts = <String>[];
|
||||||
|
|
||||||
|
await AppConfig.probeAndApplyBaseUrl(
|
||||||
|
fallbackBaseUrl: AppConfig.devHostBaseUrl,
|
||||||
|
probe: (baseUrl) async {
|
||||||
|
attempts.add(baseUrl);
|
||||||
|
return baseUrl == AppConfig.lanBaseUrl;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(attempts, [AppConfig.lanBaseUrl]);
|
||||||
|
expect(AppConfig.apiBaseUrl, AppConfig.lanBaseUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should_probe_loopback_dev_host_then_public_in_order', () async {
|
||||||
|
final attempts = <String>[];
|
||||||
|
|
||||||
|
await AppConfig.probeAndApplyBaseUrl(
|
||||||
|
fallbackBaseUrl: AppConfig.devHostBaseUrl,
|
||||||
|
probe: (baseUrl) async {
|
||||||
|
attempts.add(baseUrl);
|
||||||
|
return baseUrl == AppConfig.publicBaseUrl;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(attempts, [
|
||||||
|
AppConfig.lanBaseUrl,
|
||||||
|
AppConfig.devHostBaseUrl,
|
||||||
|
AppConfig.publicBaseUrl,
|
||||||
|
]);
|
||||||
|
expect(AppConfig.apiBaseUrl, AppConfig.publicBaseUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should_use_platform_default_when_all_probes_fail', () async {
|
||||||
|
final attempts = <String>[];
|
||||||
|
|
||||||
|
await AppConfig.probeAndApplyBaseUrl(
|
||||||
|
fallbackBaseUrl: AppConfig.devHostBaseUrl,
|
||||||
|
probe: (baseUrl) async {
|
||||||
|
attempts.add(baseUrl);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(attempts, [
|
||||||
|
AppConfig.lanBaseUrl,
|
||||||
|
AppConfig.devHostBaseUrl,
|
||||||
|
AppConfig.publicBaseUrl,
|
||||||
|
]);
|
||||||
|
expect(AppConfig.apiBaseUrl, AppConfig.devHostBaseUrl);
|
||||||
|
});
|
||||||
|
|
||||||
test('should_apply_stored_url_only_when_debug_is_enabled', () async {
|
test('should_apply_stored_url_only_when_debug_is_enabled', () async {
|
||||||
SharedPreferencesAsyncPlatform.instance =
|
SharedPreferencesAsyncPlatform.instance =
|
||||||
InMemorySharedPreferencesAsync.withData({
|
InMemorySharedPreferencesAsync.withData({
|
||||||
@@ -81,10 +134,7 @@ void main() {
|
|||||||
isNull,
|
isNull,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(await ServerEndpoint.save(' http://192.168.31.187:8081/ '), isTrue);
|
||||||
await ServerEndpoint.save(' http://192.168.31.187:8081/ '),
|
|
||||||
isTrue,
|
|
||||||
);
|
|
||||||
expect(
|
expect(
|
||||||
await SharedPreferencesAsync().getString(ServerEndpoint.preferenceKey),
|
await SharedPreferencesAsync().getString(ServerEndpoint.preferenceKey),
|
||||||
'http://192.168.31.187:8081',
|
'http://192.168.31.187:8081',
|
||||||
|
|||||||
Reference in new issue
Block a user