Files
proxypin/lib/network/components/request_rewrite.dart
wanghongenpin 853348f1d5 hex viewer
2025-09-09 19:50:50 +08:00

296 lines
10 KiB
Dart

/*
* Copyright 2024 Hongen Wang All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'dart:collection';
import 'dart:convert';
import 'package:proxypin/network/components/interceptor.dart';
import 'package:proxypin/network/components/manager/request_rewrite_manager.dart';
import 'package:proxypin/network/http/constants.dart';
import 'package:proxypin/network/http/http.dart';
import 'package:proxypin/network/http/http_headers.dart';
import 'package:proxypin/network/util/file_read.dart';
import 'package:proxypin/network/util/logger.dart';
import 'package:proxypin/network/util/uri.dart';
import 'package:proxypin/utils/lang.dart';
import 'manager/rewrite_rule.dart';
/// RequestRewriteComponent is a component that can rewrite the request before sending it to the server.
/// @author Hongen Wang
class RequestRewriteInterceptor extends Interceptor {
static RequestRewriteInterceptor instance = RequestRewriteInterceptor._();
final requestRewriteManager = RequestRewriteManager.instance;
RequestRewriteInterceptor._();
@override
Future<HttpRequest?> onRequest(HttpRequest request) async {
//重写请求
var url = request.requestUrl;
await requestRewrite(url, request);
return request;
}
@override
Future<HttpResponse?> onResponse(HttpRequest request, HttpResponse response) async {
//重写响应
try {
var url = request.requestUrl;
await responseRewrite(url, response);
} catch (e, t) {
response.body = "$e".codeUnits;
logger.e('[${request.requestId}] 响应重写异常 ', error: e, stackTrace: t);
}
return response;
}
///获取重定向
Future<String?> getRedirectRule(String? url) async {
var manager = await requestRewriteManager;
var rewriteRule = manager.getRewriteRule(url, [RuleType.redirect]);
if (rewriteRule == null) {
return null;
}
var rewriteItems = await manager.getRewriteItems(rewriteRule);
var redirectUrl = rewriteItems?.firstWhereOrNull((element) => element.enabled)?.redirectUrl;
if (rewriteRule.url.contains("*") && redirectUrl?.contains("*") == true) {
String ruleUrl = rewriteRule.url.replaceAll("*", "");
redirectUrl = redirectUrl?.replaceAll("*", url!.replaceAll(ruleUrl, ""));
}
return redirectUrl;
}
/// 重写请求
Future<void> requestRewrite(String url, HttpRequest request) async {
var manager = await RequestRewriteManager.instance;
var rewriteRule = manager.getRewriteRule(url, [RuleType.requestReplace, RuleType.requestUpdate]);
if (rewriteRule?.type == RuleType.requestReplace) {
var rewriteItems = await manager.getRewriteItems(rewriteRule!);
for (var item in rewriteItems!) {
if (item.enabled) {
await _replaceRequest(request, item);
}
}
}
if (rewriteRule?.type == RuleType.requestUpdate) {
var rewriteItems = await manager.getRewriteItems(rewriteRule!);
if (rewriteItems == null) {
return;
}
for (var item in rewriteItems) {
if (item.enabled) {
await _updateRequest(request, item);
}
}
}
}
/// 重写响应
Future<void> responseRewrite(String? url, HttpResponse response) async {
var manager = await RequestRewriteManager.instance;
var rewriteRule = manager.getRewriteRule(url, [RuleType.responseReplace, RuleType.responseUpdate]);
if (rewriteRule == null) {
return;
}
if (rewriteRule.type == RuleType.responseReplace) {
var rewriteItems = await manager.getRewriteItems(rewriteRule);
for (var item in rewriteItems!) {
if (item.enabled) {
await _replaceResponse(response, item);
}
}
}
if (rewriteRule.type == RuleType.responseUpdate) {
var rewriteItems = await manager.getRewriteItems(rewriteRule);
if (rewriteItems == null) {
return;
}
for (var item in rewriteItems) {
if (item.enabled) {
await _updateMessage(response, item);
}
}
}
}
Future<void> _updateRequest(HttpRequest request, RewriteItem item) async {
var paramTypes = [RewriteType.addQueryParam, RewriteType.removeQueryParam, RewriteType.updateQueryParam];
if (paramTypes.contains(item.type)) {
var requestUri = request.requestUri;
Map<String, dynamic> queryParameters = LinkedHashMap.from(requestUri!.queryParameters);
switch (item.type) {
case RewriteType.addQueryParam:
queryParameters[item.key!] = item.value;
break;
case RewriteType.removeQueryParam:
if (item.value?.trim().isNotEmpty == true) {
var val = queryParameters[item.key!];
if (val == null || !RegExp(item.value!).hasMatch(val)) {
break;
}
}
queryParameters.remove(item.key!);
break;
case RewriteType.updateQueryParam:
var itemKey = item.key;
if (itemKey == null || itemKey.trim().isEmpty) return;
var entries = Map.of(queryParameters).entries;
var regExp = RegExp(item.key!);
for (var entry in entries) {
var line = "${entry.key}=${entry.value}";
if (regExp.hasMatch(line)) {
line = line.replaceAll(regExp, item.value ?? '');
var pair = line.splitFirst(HttpConstants.equal);
if (pair.first != entry.key) queryParameters.remove(entry.key);
queryParameters[pair.first] = pair.length > 1 ? pair.last : '';
break;
}
}
break;
default:
break;
}
requestUri = requestUri.replace(query: UriUtils.mapToQuery(queryParameters));
if (requestUri.isScheme('https')) {
request.uri = requestUri.path + (requestUri.hasQuery ? "?${requestUri.query}" : "");
} else {
request.uri = requestUri.toString();
}
return;
}
await _updateMessage(request, item);
}
//修改消息
Future<void> _updateMessage(HttpMessage message, RewriteItem item) async {
if (item.type == RewriteType.updateBody && message.body != null) {
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)!);
}
return item.value ?? '';
});
message.body = message.charset == 'utf-8' || message.charset == 'utf8' ? utf8.encode(body) : body.codeUnits;
message.headers.remove(HttpHeaders.CONTENT_ENCODING);
message.headers.contentLength = message.body!.length;
return;
}
if (item.type == RewriteType.addHeader) {
message.headers.set(item.key!, item.value ?? '');
return;
}
if (item.type == RewriteType.removeHeader) {
if (item.value?.trim().isNotEmpty == true) {
var val = message.headers.get(item.key!);
if (val == null || !RegExp(item.value!).hasMatch(val)) {
return;
}
}
message.headers.remove(item.key!);
return;
}
if (item.type == RewriteType.updateHeader) {
if (item.key == null || item.key?.trim().isEmpty == true) return;
var headers = Map.of(message.headers.getHeaders());
var regExp = RegExp(item.key!, caseSensitive: false);
headers.forEach((key, values) {
var line = "$key: ${values.firstOrNull ?? ''}";
if (regExp.hasMatch(line)) {
line = line.replaceAll(regExp, item.value ?? '');
var pair = line.splitFirst(HttpConstants.colon);
if (pair.first != key) message.headers.remove(key);
message.headers.set(pair.first, pair.length > 1 ? pair.last : '');
}
});
return;
}
}
//替换请求
Future<void> _replaceRequest(HttpRequest request, RewriteItem item) async {
if (item.type == RewriteType.replaceRequestLine) {
request.method = item.method ?? request.method;
Uri uri = Uri.parse(request.requestUrl).replace(path: item.path, query: item.queryParam);
if (uri.isScheme('https')) {
request.uri = uri.path + (uri.hasQuery ? "?${uri.query}" : "");
} else {
request.uri = uri.toString();
}
return;
}
await _replaceHttpMessage(request, item);
}
//替换相应
Future<void> _replaceResponse(HttpResponse response, RewriteItem item) async {
if (item.type == RewriteType.replaceResponseStatus && item.statusCode != null) {
response.status = HttpStatus.valueOf(item.statusCode!);
return;
}
await _replaceHttpMessage(response, item);
}
Future<void> _replaceHttpMessage(HttpMessage message, RewriteItem item) async {
if ((item.type == RewriteType.replaceRequestHeader || item.type == RewriteType.replaceResponseHeader) &&
item.headers != null) {
item.headers?.forEach((key, value) => message.headers.set(key, value));
return;
}
if (item.type == RewriteType.replaceResponseBody || item.type == RewriteType.replaceRequestBody) {
if (item.bodyType == ReplaceBodyType.file.name) {
if (item.bodyFile == null) return;
message.body = await FileRead.readFile(item.bodyFile!);
message.headers.contentLength = message.body!.length;
message.headers.remove(HttpHeaders.CONTENT_ENCODING);
return;
}
if (item.body != null) {
message.body =
message.charset == 'utf-8' || message.charset == 'utf8' ? utf8.encode(item.body!) : item.body?.codeUnits;
message.headers.contentLength = message.body!.length;
message.headers.remove(HttpHeaders.CONTENT_ENCODING);
}
return;
}
}
}