chunk编码

This commit is contained in:
wanghongen
2023-06-19 19:43:03 +08:00
parent 4f6192a96d
commit 8ea2fdee67
11 changed files with 163 additions and 87 deletions

View File

@@ -17,7 +17,7 @@ void main() async {
await windowManager.ensureInitialized();
WindowOptions windowOptions = WindowOptions(
minimumSize: const Size(930, 500),
size: const Size(1200, 700),
size: const Size(1080, 700),
center: true,
titleBarStyle: Platform.isMacOS ? TitleBarStyle.hidden : TitleBarStyle.normal);
windowManager.waitUntilReadyToShow(windowOptions, () async {

View File

@@ -82,7 +82,7 @@ class ProxyServer {
_concatCommands([
'networksetup -setwebproxy wi-fi $host $port',
enableSsl ? 'networksetup -setsecurewebproxy wi-fi $host $port' : '',
'networksetup -setproxybypassdomains wi-fi 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 127.0.0.1, localhost, *.local, timestamp.apple.com, sequoia.apple.com, seed-sequoia.siri.apple.com',
'networksetup -setproxybypassdomains wi-fi 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 127.0.0.1, localhost, *.local, timestamp.apple.com, sequoia.apple.com, seed-sequoia.siri.apple.com, *.google.com, *.googleapis.com',
])
]);
print('set proxyServer, exitCode: ${results.exitCode}, stdout: ${results.stdout}');

View File

@@ -245,7 +245,7 @@ class Network {
_channelInitializer.call(channel);
channel.pipeline.channelActive(channel);
socket.listen((data) => _onEvent(data, channel),
onError: (error, trace) => channel.pipeline.exceptionCaught(channel, error, trace: trace),
onError: (error, StackTrace trace) => channel.pipeline.exceptionCaught(channel, error, trace: trace),
onDone: () => channel.pipeline.channelInactive(channel));
return channel;
}

View File

@@ -34,10 +34,10 @@ class HttpChannelHandler extends ChannelHandler<HttpRequest> {
@override
void channelRead(Channel channel, HttpRequest msg) async {
forward(channel, msg).catchError((error, trace) {
channel.close();
if (error is SocketException &&
(error.message.contains("Failed host lookup") || error.message.contains("Connection timed out"))) {
log.e("连接失败 ${error.message}");
channel.close();
return;
}
log.e("转发请求失败", error, trace);
@@ -46,7 +46,6 @@ class HttpChannelHandler extends ChannelHandler<HttpRequest> {
@override
void channelInactive(Channel channel) {
super.channelInactive(channel);
Channel? remoteChannel = channel.getAttribute(channel.id);
remoteChannel?.close();
}
@@ -77,7 +76,7 @@ class HttpChannelHandler extends ChannelHandler<HttpRequest> {
}
}
void _crtDownload(Channel channel, HttpRequest request) async{
void _crtDownload(Channel channel, HttpRequest request) async {
const String fileMimeType = 'application/x-x509-ca-cert';
var body = await rootBundle.load('assets/certs/ca.crt');
var response = HttpResponse(request.protocolVersion, HttpStatus.ok);
@@ -145,6 +144,7 @@ class RelayHandler extends ChannelHandler<Object> {
//发送给客户端
remoteChannel.write(msg);
}
@override
void channelInactive(Channel channel) {
remoteChannel.close();

View File

@@ -0,0 +1,94 @@
import 'dart:math';
import 'dart:typed_data';
import '../../utils/num.dart';
import 'codec.dart';
///一个ChunkedInput它逐块获取数据用于HTTP分块传输。
/// 请确保您的HTTP响应标头包含传输编码chunked。
class ChunkedInput {
final BytesBuilder _buffer = BytesBuilder();
///chunked编码 剩余未读取的chunk大小
int _chunkReadableSize = 0;
int _offset = 0;
ChunkedState _state = ChunkedState.readChunkSize;
///读取chunk
ChunkedContent readChunked(Uint8List data) {
_offset = 0;
while (_offset < data.length) {
//读取chunk length
if (_state == ChunkedState.readChunkSize) {
_chunkReadableSize = _readChunkSize(data);
if (_chunkReadableSize == 0) {
//chunked编码结束
_state = ChunkedState.done;
break;
}
if (_chunkReadableSize == -1) {
continue;
}
_state = ChunkedState.readChunkedContent;
}
//读取chunk内容
if (_state == ChunkedState.readChunkedContent) {
int end = min(data.length, _offset + _chunkReadableSize);
_buffer.add(data.sublist(_offset, end));
//可读大小
_chunkReadableSize -= (end - _offset);
_offset = end;
if (_chunkReadableSize == 0) {
_state = ChunkedState.readChunkSize;
_offset += 2; //内容结尾\r\n
}
}
}
return ChunkedContent(_state, _buffer.toBytes());
}
int _readChunkSize(Uint8List data) {
var line = parseLine(data);
if (line.isEmpty) {
return -1;
}
return hexToInt(String.fromCharCodes(line));
}
Uint8List parseLine(Uint8List data) {
if (_offset >= data.length) {
return Uint8List(0);
}
for (int i = _offset; i < data.length; i++) {
if (_isLineEnd(data, i)) {
var line = data.sublist(_offset, i - 1);
_offset = i + 1;
return line;
}
}
throw Exception('Invalid chunked encoding line: ${String.fromCharCodes(data)}');
}
bool _isLineEnd(List<int> data, int index) {
return data[index] == HttpConstants.lf && data[index - 1] == HttpConstants.cr;
}
}
enum ChunkedState { readChunkSize, readChunkedContent, done }
class ChunkedContent {
final ChunkedState state;
final Uint8List content;
ChunkedContent(this.state, this.content);
}

View File

@@ -1,10 +1,10 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:network/utils/num.dart';
import 'package:network/network/http/chunked_codec.dart';
import '../channel.dart';
import '../../utils/compress.dart';
import '../channel.dart';
import 'http.dart';
import 'http_headers.dart';
@@ -25,9 +25,8 @@ class HttpConstants {
enum State {
readInitial,
readHeader,
readVariableLengthContent,
readFixedLengthContent,
readChunkedContent,
readChunked,
done,
}
@@ -36,19 +35,20 @@ abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
final HttpParse _httpParse = HttpParse();
State _state = State.readInitial;
///chunked编码 剩余未读取的chunk大小
int _chunkReadableSize = 0;
late T message;
final BytesBuilder _bodyBuffer = BytesBuilder();
ChunkedInput? chunkedInput;
final BytesBuilder buffer = BytesBuilder();
T createMessage(List<String> reqLine);
@override
T? decode(Uint8List data) {
_httpParse.index = 0;
//请求行
if (_state == State.readInitial) {
init();
var initialLine = _readInitialLine(data);
message = createMessage(initialLine);
_state = State.readHeader;
@@ -57,28 +57,33 @@ abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
//请求头
if (_state == State.readHeader) {
_readHeader(data, message);
_state = message.headers.isChunked ? State.readVariableLengthContent : State.readFixedLengthContent;
_state = message.headers.isChunked ? State.readChunked : State.readFixedLengthContent;
}
//chunked编码
if (_state == State.readChunkedContent) {
_bodyBuffer.add(data.sublist(0, min(_chunkReadableSize, data.length)));
if (data.length < _chunkReadableSize) {
_chunkReadableSize = _chunkReadableSize - data.length;
return null;
//固定长度请求体
if (_state == State.readFixedLengthContent) {
if (message.contentLength > 0) {
buffer.add(data.sublist(_httpParse.index));
}
_httpParse.index = _chunkReadableSize + 2;
_state = State.readVariableLengthContent; //读取下一个chunk
}
//请求体
if (_state == State.readFixedLengthContent || _state == State.readVariableLengthContent) {
_readBody(data, message);
if ((message.contentLength == -1 && !message.headers.isChunked) || _bodyBuffer.length == message.contentLength) {
if (message.contentLength == -1 || buffer.length >= message.contentLength) {
message.body = buffer.length == 0 ? null : buffer.toBytes();
buffer.clear();
_state = State.done;
}
}
//chunked编码
if (_state == State.readChunked) {
ChunkedContent content = chunkedInput!.readChunked(data.sublist(_httpParse.index));
if (content.state == ChunkedState.done) {
message.body = content.content;
_state = State.done;
}
}
if (_state == State.done) {
message.body = _convertBody();
message.body = _convertBody(message.body);
_state = State.readInitial;
return message;
}
@@ -86,6 +91,11 @@ abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
return null;
}
void init() {
_httpParse.reset();
buffer.clear();
chunkedInput = ChunkedInput();
}
void initialLine(BytesBuilder buffer, T message);
@override
@@ -123,7 +133,6 @@ abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
//读取起始行
List<String> _readInitialLine(Uint8List data) {
_httpParse.reset();
int maxSize = min(data.length, Codec.defaultMaxInitialLineLength);
return _httpParse.parseInitialLine(data, maxSize);
}
@@ -134,43 +143,14 @@ abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
message.contentLength = message.headers.contentLength;
}
//读取请求体
void _readBody(Uint8List data, T message) {
if (_state == State.readVariableLengthContent) {
while (_httpParse.index < data.length) {
var parseLine = _httpParse.parseLine(data);
if (parseLine.isEmpty) {
_httpParse.index = 0;
return;
}
int length = hexToInt(String.fromCharCodes(parseLine));
//chunked编码结束 最后以length = 0 结束
if (length == 0) {
_state = State.done;
return;
}
_bodyBuffer.add(data.sublist(_httpParse.index, min(_httpParse.index + length, data.length)));
if (_httpParse.index + length > data.length) {
_state = State.readChunkedContent;
_chunkReadableSize = length - (data.length - _httpParse.index);
}
_httpParse.index += length + 2; //跳过\r\n
}
_httpParse.index = 0;
} else if (message.contentLength > 0) {
_bodyBuffer.add(data.sublist(_httpParse.index));
_httpParse.index = 0;
}
}
//转换body
List<int> _convertBody() {
List<int> bytes = _bodyBuffer.toBytes();
if (message.headers.isGzip) {
bytes = gzipDecode(bytes);
List<int>? _convertBody(List<int>? bytes) {
if (bytes == null) {
return null;
}
if (message.headers.isGzip) {
bytes = gzipDecode(bytes);
}
_bodyBuffer.clear();
return bytes;
}
}

View File

@@ -7,7 +7,7 @@ import 'package:path_provider/path_provider.dart';
import 'logger.dart';
void main() {
print(HostFilter.filter("www.apple.com"));
print(HostFilter.filter("stackoverflow.com"));
}
class HostFilter {
@@ -25,7 +25,7 @@ class HostFilter {
//如果白名单不为空,不在白名单里都是黑名单
if (whites.enabled) {
return whites.list.any((element) => !element.hasMatch(host));
return whites.list.every((element) => !element.hasMatch(host));
}
if (blacklist.enabled) {
return blacklist.list.any((element) => element.hasMatch(host));
@@ -46,7 +46,7 @@ abstract class HostList {
final List<RegExp> list = [];
bool enabled = false;
List<Function> _initListens = [];
final List<Function> _initListens = [];
bool _inited = false;
void addInitListen(void Function() action) {

View File

@@ -33,15 +33,15 @@ class _SocketLaunchState extends State<SocketLaunch> with WindowListener {
@override
void onWindowMinimize() {
started = false;
setState(() {});
widget.proxyServer.stop();
setState(() {});
}
@override
void onWindowClose() {
started = false;
setState(() {});
widget.proxyServer.stop();
setState(() {});
}
@override

View File

@@ -72,28 +72,26 @@ class _SslState extends State<SslWidget> {
showDialog(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
contentPadding: const EdgeInsets.all(16),
title: const Text("手机https抓包配置", style: TextStyle(fontSize: 16)),
return const SimpleDialog(
contentPadding: EdgeInsets.all(16),
title: Text("手机https抓包配置", style: TextStyle(fontSize: 16)),
alignment: Alignment.center,
children: [
const Text("1. 根证书安装到本系统(已完成忽略)"),
const SizedBox(height: 10),
const Text.rich(TextSpan(text: "2.配置手机Wifi代理 ")),
const SizedBox(height: 10),
Text("1. 根证书安装到本系统(已完成忽略)"),
SizedBox(height: 10),
Text.rich(TextSpan(text: "2.配置手机Wifi代理 ")),
SizedBox(height: 10),
Row(
children: [
const Text("3.打开手机系统自带浏览器访问:"),
TextButton(
onPressed: () {
launchUrl(Uri.parse("http://proxy.pin/ssl"));
},
child:
const Text("http://proxy.pin/ssl", style: TextStyle(decoration: TextDecoration.underline)))
Text("3.打开手机系统自带浏览器访问\t"),
SelectableText.rich(
TextSpan(text: "http://proxy.pin/ssl", style: TextStyle(decoration: TextDecoration.underline)))
],
),
const SizedBox(height: 10),
const Text("4.打开手机设置下载安装证书(Profile)和信任证书(Certificate) \n\t 设置 > 通用 > 关于本机 > 证书信任设置"),
SizedBox(height: 10),
Text("4.打开手机设置下载安装证书(Profile)和信任证书(Certificate) \n\t 设置 > 通用 > 关于本机 > 证书信任设置"),
SizedBox(height: 20),
Text(" 微信小程序ios需要开启本地网络权限", style: TextStyle(fontWeight: FontWeight.bold)),
]);
});
}

View File

@@ -13,7 +13,7 @@ int hexToInt(String hex) {
// a..f
val += (hexDigit - 87) * (1 << (4 * (len - 1 - i)));
} else {
throw const FormatException("Invalid hexadecimal value");
throw FormatException("Invalid hexadecimal value $hex");
}
}
return val;

4
test/tests.dart Normal file
View File

@@ -0,0 +1,4 @@
void main() {
RegExp reg = RegExp("stackoverflow.com");
print(reg.hasMatch("stackoverflow.com"));
}