enable http2 (#331)

This commit is contained in:
My-Responsitories
2025-02-26 14:02:19 +00:00
committed by GitHub
parent 1c3d77b95d
commit 0a6950e34a
8 changed files with 112 additions and 84 deletions

View File

@@ -23,8 +23,8 @@ class Constants {
static const String userAgent = static const String userAgent =
'Mozilla/5.0 BiliDroid/1.46.2 (bbcallen@gmail.com) os/android model/vivo mobi_app/android_hd build/2001100 channel/yingyongbao innerVer/2001100 osVer/14 network/2'; 'Mozilla/5.0 BiliDroid/1.46.2 (bbcallen@gmail.com) os/android model/vivo mobi_app/android_hd build/2001100 channel/yingyongbao innerVer/2001100 osVer/14 network/2';
static const String statistics = static const String statistics =
'%7B%22appId%22%3A5%2C%22platform%22%3A3%2C%22version%22%3A%221.46.2%22%2C%22abtest%22%3A%22%22%7D'; '{"appId":5,"platform":3,"version":"1.46.2","abtest":""}';
//Uri.encodeComponent('{"appId": 5,"platform": 3,"version": "1.46.2","abtest": ""}'); // 请求时会自动encodeComponent
static const urlPattern = static const urlPattern =
r'https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]'; r'https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]';

View File

@@ -619,7 +619,7 @@ class Api {
/// 申请二维码(TV端) /// 申请二维码(TV端)
static const getTVCode = static const getTVCode =
'https://passport.bilibili.com/x/passport-tv-login/qrcode/auth_code'; '${HttpString.passBaseUrl}/x/passport-tv-login/qrcode/auth_code';
///扫码登录TV端 ///扫码登录TV端
static const qrcodePoll = static const qrcodePoll =
@@ -662,7 +662,7 @@ class Api {
static const getUnreadDynamic = '/x/web-interface/dynamic/entrance'; static const getUnreadDynamic = '/x/web-interface/dynamic/entrance';
/// 用户动态主页 /// 用户动态主页
static const dynamicSpmPrefix = 'https://space.bilibili.com/1/dynamic'; static const dynamicSpmPrefix = '${HttpString.spaceBaseUrl}/1/dynamic';
/// 激活buvid3 /// 激活buvid3
static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi'; static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi';

View File

@@ -4,10 +4,13 @@ import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'dart:math' show Random; import 'dart:math' show Random;
import 'package:PiliPlus/build_config.dart'; import 'package:PiliPlus/build_config.dart';
import 'package:archive/archive.dart';
import 'package:brotli/brotli.dart';
import 'package:cookie_jar/cookie_jar.dart'; import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:dio/io.dart'; import 'package:dio/io.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart'; import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:PiliPlus/utils/id_utils.dart'; import 'package:PiliPlus/utils/id_utils.dart';
import '../utils/storage.dart'; import '../utils/storage.dart';
@@ -18,6 +21,9 @@ import 'interceptor.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart' as web; import 'package:flutter_inappwebview/flutter_inappwebview.dart' as web;
class Request { class Request {
static const gzipDecoder = GZipDecoder();
static const brotilDecoder = BrotliDecoder();
static final Request _instance = Request._internal(); static final Request _instance = Request._internal();
static late CookieManager cookieManager; static late CookieManager cookieManager;
static late final Dio dio; static late final Dio dio;
@@ -52,18 +58,18 @@ class Request {
); );
} }
final userInfo = GStorage.userInfo.get('userInfoCache'); final userInfo = GStorage.userInfo.get('userInfoCache');
if (userInfo != null && userInfo.mid != null) { if (userInfo?.mid != null) {
final List<Cookie> cookie2 = await cookieManager.cookieJar final List<Cookie> tUrlCookies = await cookieManager.cookieJar
.loadForRequest(Uri.parse(HttpString.tUrl)); .loadForRequest(Uri.parse(HttpString.tUrl));
if (cookie2.isEmpty) { if (tUrlCookies.isEmpty) {
try { try {
await Request().get(HttpString.tUrl); await dio.head(HttpString.tUrl);
} catch (e) { } catch (e) {
log("setCookie, ${e.toString()}"); log("setCookie, ${e.toString()}");
} }
} }
setOptionsHeaders(userInfo);
} }
setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null);
try { try {
await buvidActivate(); await buvidActivate();
@@ -81,24 +87,16 @@ class Request {
static Future<String> getCsrf() async { static Future<String> getCsrf() async {
List<Cookie> cookies = await cookieManager.cookieJar List<Cookie> cookies = await cookieManager.cookieJar
.loadForRequest(Uri.parse(HttpString.apiBaseUrl)); .loadForRequest(Uri.parse(HttpString.apiBaseUrl));
String token = ''; return cookies
if (cookies.where((e) => e.name == 'bili_jct').isNotEmpty) { .firstWhere((e) => e.name == 'bili_jct', orElse: () => Cookie('', ''))
token = cookies.firstWhere((e) => e.name == 'bili_jct').value; .value;
}
return token;
} }
static setOptionsHeaders(userInfo, bool status) { static setOptionsHeaders(userInfo) {
if (status) {
dio.options.headers['x-bili-mid'] = userInfo.mid.toString(); dio.options.headers['x-bili-mid'] = userInfo.mid.toString();
dio.options.headers['x-bili-aurora-eid'] = dio.options.headers['x-bili-aurora-eid'] =
IdUtils.genAuroraEid(userInfo.mid); IdUtils.genAuroraEid(userInfo.mid);
} }
dio.options.headers['env'] = 'prod';
dio.options.headers['app-key'] = 'android64';
dio.options.headers['x-bili-aurora-zone'] = 'sh001';
dio.options.headers['referer'] = 'https://www.bilibili.com/';
}
static Future buvidActivate() async { static Future buvidActivate() async {
var html = await Request().get(Api.dynamicSpmPrefix); var html = await Request().get(Api.dynamicSpmPrefix);
@@ -121,7 +119,7 @@ class Request {
await Request().post(Api.activateBuvidApi, await Request().post(Api.activateBuvidApi,
data: {'payload': jsonData}, data: {'payload': jsonData},
options: Options(contentType: 'application/json')); options: Options(contentType: Headers.jsonContentType));
} }
/* /*
@@ -137,8 +135,17 @@ class Request {
//响应流上前后两次接受到数据的间隔,单位为毫秒。 //响应流上前后两次接受到数据的间隔,单位为毫秒。
receiveTimeout: const Duration(milliseconds: 12000), receiveTimeout: const Duration(milliseconds: 12000),
//Http请求头. //Http请求头.
headers: {}, headers: {
); 'connection': 'keep-alive',
'accept-encoding': 'br,gzip',
'user-agent': 'Dart/3.6 (dart:io)', // Http2Adapter不会自动添加标头
'referer': HttpString.baseUrl,
'env': 'prod',
'app-key': 'android64',
'x-bili-aurora-zone': 'sh001',
},
responseDecoder: responseDecoder, // Http2Adapter没有自动解压
persistentConnection: true);
enableSystemProxy = GStorage.setting enableSystemProxy = GStorage.setting
.get(SettingBoxKey.enableSystemProxy, defaultValue: false) as bool; .get(SettingBoxKey.enableSystemProxy, defaultValue: false) as bool;
@@ -147,33 +154,36 @@ class Request {
systemProxyPort = systemProxyPort =
GStorage.setting.get(SettingBoxKey.systemProxyPort, defaultValue: ''); GStorage.setting.get(SettingBoxKey.systemProxyPort, defaultValue: '');
dio = Dio(options); final http11Adapter = IOHttpClientAdapter(createHttpClient: () {
final client = HttpClient()
/// fix 第三方登录 302重定向 跟iOS代理问题冲突 ..idleTimeout = const Duration(seconds: 30)
// ..httpClientAdapter = Http2Adapter( ..autoUncompress = false; // Http2Adapter没有自动解压, 统一行为
// ConnectionManager( // 设置代理
// idleTimeout: const Duration(milliseconds: 10000),
// onClientCreate: (context, ClientSetting config) =>
// config.onBadCertificate = (_) => true,
// ),
// );
/// 设置代理
if (enableSystemProxy) { if (enableSystemProxy) {
dio.httpClientAdapter = IOHttpClientAdapter( client.findProxy = (_) => 'PROXY $systemProxyHost:$systemProxyPort';
createHttpClient: () {
final HttpClient client = HttpClient();
// Config the client.
client.findProxy = (Uri uri) {
// return 'PROXY host:port';
return 'PROXY $systemProxyHost:$systemProxyPort';
};
client.badCertificateCallback = client.badCertificateCallback =
(X509Certificate cert, String host, int port) => true; (X509Certificate cert, String host, int port) => true;
return client;
},
);
} }
return client;
});
dio = Dio(options)
..httpClientAdapter =
GStorage.setting.get(SettingBoxKey.enableHttp2, defaultValue: false)
? Http2Adapter(
ConnectionManager(
idleTimeout: const Duration(seconds: 30),
onClientCreate: (_, ClientSetting config) {
config.onBadCertificate = (_) => true;
if (enableSystemProxy) {
config.proxy = Uri(
scheme: 'http',
host: systemProxyHost,
port: int.parse(systemProxyPort));
}
}),
fallbackAdapter: http11Adapter)
: http11Adapter;
// 日志拦截器 输出请求、响应内容 // 日志拦截器 输出请求、响应内容
if (BuildConfig.isDebug) { if (BuildConfig.isDebug) {
@@ -186,8 +196,7 @@ class Request {
dio.transformer = BackgroundTransformer(); dio.transformer = BackgroundTransformer();
dio.options.validateStatus = (int? status) { dio.options.validateStatus = (int? status) {
return status! >= 200 && status < 300 || return status! >= 200 && status < 300;
HttpString.validateStatusCodes.contains(status);
}; };
} }
@@ -285,19 +294,24 @@ class Request {
} }
static String headerUa({type = 'mob'}) { static String headerUa({type = 'mob'}) {
String headerUa = ''; return type == 'mob'
if (type == 'mob') { ? Platform.isIOS
if (Platform.isIOS) { ? 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Mobile/15E148 Safari/604.1'
headerUa = : 'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36'
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Mobile/15E148 Safari/604.1'; : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15';
} else {
headerUa =
'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36';
} }
} else {
headerUa = static String responseDecoder(List<int> responseBytes, RequestOptions options,
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15'; ResponseBody responseBody) {
switch (responseBody.headers['content-encoding']?.firstOrNull) {
case 'gzip':
return utf8.decode(gzipDecoder.decodeBytes(responseBytes),
allowMalformed: true);
case 'br':
return utf8.decode(brotilDecoder.convert(responseBytes),
allowMalformed: true);
default:
return utf8.decode(responseBytes, allowMalformed: true);
} }
return headerUa;
} }
} }

View File

@@ -32,7 +32,7 @@ class ApiInterceptor extends Interceptor {
options.headers.remove('x-bili-mid'); options.headers.remove('x-bili-mid');
options.headers.remove('x-bili-aurora-eid'); options.headers.remove('x-bili-aurora-eid');
options.headers.remove('x-bili-aurora-zone'); options.headers.remove('x-bili-aurora-zone');
options.headers['cookie'] = ''; options.headers.remove('cookie');
options.queryParameters.remove('access_key'); options.queryParameters.remove('access_key');
options.queryParameters.remove('csrf'); options.queryParameters.remove('csrf');
options.queryParameters.remove('csrf_token'); options.queryParameters.remove('csrf_token');
@@ -45,7 +45,7 @@ class ApiInterceptor extends Interceptor {
// app端不需要cookie // app端不需要cookie
if (options.uri.host == 'app.bilibili.com') { if (options.uri.host == 'app.bilibili.com') {
options.headers['cookie'] = ''; options.headers.remove('cookie');
} }
if (options.extra['clearCookie'] == true) { if (options.extra['clearCookie'] == true) {
@@ -139,5 +139,5 @@ class ApiInterceptor extends Interceptor {
} }
extension _ConnectivityResultExt on ConnectivityResult { extension _ConnectivityResultExt on ConnectivityResult {
String get title => ['蓝牙', 'Wi-Fi', '局域', '流量', '', '代理', '其他'][index]; String get title => const ['蓝牙', 'Wi-Fi', '局域', '流量', '', '代理', '其他'][index];
} }

View File

@@ -120,7 +120,6 @@ class VideoHttp {
Api.recommendListApp, Api.recommendListApp,
queryParameters: data, queryParameters: data,
options: Options(headers: { options: Options(headers: {
'Host': 'app.bilibili.com',
'buvid': LoginHttp.buvid, 'buvid': LoginHttp.buvid,
'fp_local': 'fp_local':
'1111111111111111111111111111111111111111111111111111111111111111', '1111111111111111111111111111111111111111111111111111111111111111',

View File

@@ -54,9 +54,7 @@ void main() async {
], ],
); );
} }
if (BuildConfig.isDebug || GStorage.badCertificateCallback) {
HttpOverrides.global = _CustomHttpOverrides(); HttpOverrides.global = _CustomHttpOverrides();
}
await setupServiceLocator(); await setupServiceLocator();
Request(); Request();
await Request.setCookie(); await Request.setCookie();
@@ -294,10 +292,18 @@ class MyApp extends StatelessWidget {
} }
class _CustomHttpOverrides extends HttpOverrides { class _CustomHttpOverrides extends HttpOverrides {
static final badCertificateCallback =
BuildConfig.isDebug || GStorage.badCertificateCallback;
@override @override
HttpClient createHttpClient(SecurityContext? context) { HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context) final client = super.createHttpClient(context)
..badCertificateCallback = ..maxConnectionsPerHost = 32
..idleTimeout = const Duration(seconds: 30);
if (badCertificateCallback) {
client.badCertificateCallback =
(X509Certificate cert, String host, int port) => true; (X509Certificate cert, String host, int port) => true;
} }
return client;
}
} }

View File

@@ -2181,6 +2181,14 @@ List<SettingsModel> get extraSettings => [
setKey: SettingBoxKey.defaultShowComment, setKey: SettingBoxKey.defaultShowComment,
defaultVal: false, defaultVal: false,
), ),
SettingsModel(
settingsType: SettingsType.sw1tch,
title: '启用HTTP/2',
subtitle: '测试中',
leading: Icon(Icons.swap_horizontal_circle_outlined),
setKey: SettingBoxKey.enableHttp2,
defaultVal: false,
),
SettingsModel( SettingsModel(
settingsType: SettingsType.normal, settingsType: SettingsType.normal,
title: '评论展示', title: '评论展示',

View File

@@ -644,6 +644,7 @@ class SettingBoxKey {
expandDynLivePanel = 'expandDynLivePanel', expandDynLivePanel = 'expandDynLivePanel',
springDescription = 'springDescription', springDescription = 'springDescription',
collapsibleVideoPage = 'collapsibleVideoPage', collapsibleVideoPage = 'collapsibleVideoPage',
enableHttp2 = 'enableHttp2',
// Sponsor Block // Sponsor Block
enableSponsorBlock = 'enableSponsorBlock', enableSponsorBlock = 'enableSponsorBlock',