http2协议

This commit is contained in:
wanghongenpin
2023-12-28 16:12:44 +08:00
parent b30cb6bde7
commit ba4072691e
30 changed files with 1850 additions and 412 deletions

View File

@@ -51,6 +51,8 @@ class Configuration {
//远程连接 不持久化保存
String? remoteHost;
bool enabledHttp2 = false; //
Configuration._();
/// 单例

View File

@@ -66,11 +66,10 @@ class ProxyServer {
/// 启动代理服务
Future<Server> start() async {
Server server = Server(configuration);
Server server = Server(configuration, listener: CombinedEventListener(listeners));
var requestRewrites = await RequestRewrites.instance;
server.initChannel((channel) {
channel.pipeline.listener = CombinedEventListener(listeners);
channel.pipeline.handle(HttpRequestCodec(), HttpResponseCodec(),
HttpProxyChannelHandler(listener: CombinedEventListener(listeners), requestRewrites: requestRewrites));
});
@@ -138,9 +137,9 @@ class CombinedEventListener extends EventListener {
}
@override
void onResponse(Channel channel, HttpResponse response) {
void onResponse(ChannelContext channelContext, HttpResponse response) {
for (var element in listeners) {
element.onResponse(channel, response);
element.onResponse(channelContext, response);
}
}

View File

@@ -21,10 +21,13 @@ import 'dart:typed_data';
import 'package:network_proxy/network/host_port.dart';
import 'package:network_proxy/network/http/codec.dart';
import 'package:network_proxy/network/http/h2/setting.dart';
import 'package:network_proxy/network/http/http.dart';
import 'package:network_proxy/network/http_client.dart';
import 'package:network_proxy/network/util/attribute_keys.dart';
import 'package:network_proxy/network/util/byte_buf.dart';
import 'package:network_proxy/network/util/logger.dart';
import 'package:network_proxy/utils/lang.dart';
import 'handler.dart';
@@ -33,19 +36,19 @@ abstract class ChannelHandler<T> {
var log = logger;
///连接建立
void channelActive(Channel channel) {}
void channelActive(ChannelContext context, Channel channel) {}
///读取数据事件
void channelRead(Channel channel, T msg) {}
void channelRead(ChannelContext channelContext, Channel channel, T msg) {}
///连接断开
void channelInactive(Channel channel) {
void channelInactive(ChannelContext channelContext, Channel channel) {
// log.i("close $channel");
}
void exceptionCaught(Channel channel, dynamic error, {StackTrace? trace}) {
HostAndPort? attribute = channel.getAttribute(AttributeKeys.host);
log.e("[${channel.id}] error $attribute $channel", error: error, stackTrace: trace);
void exceptionCaught(ChannelContext channelContext, Channel channel, dynamic error, {StackTrace? trace}) {
HostAndPort? host = channelContext.host;
log.e("[${channel.id}] error $host $channel", error: error, stackTrace: trace);
channel.close();
}
}
@@ -55,7 +58,6 @@ class Channel {
final int _id;
final ChannelPipeline pipeline = ChannelPipeline();
Socket _socket;
final Map<String, Object> _attributes = {};
//是否打开
bool isOpen = true;
@@ -79,9 +81,10 @@ class Channel {
Socket get socket => _socket;
set secureSocket(SecureSocket secureSocket) {
secureSocket(SecureSocket secureSocket, ChannelContext channelContext) {
_socket = secureSocket;
pipeline.listen(this);
_socket.done.then((value) => isOpen = false);
pipeline.listen(this, channelContext);
}
String? get selectedProtocol => isSsl ? (_socket as SecureSocket).selectedProtocol : null;
@@ -90,6 +93,11 @@ class Channel {
bool get isSsl => _socket is SecureSocket;
Future<void> write(Object obj) async {
var data = pipeline._encoder.encode(obj);
await writeBytes(data);
}
Future<void> writeBytes(List<int> bytes) async {
if (isClosed) {
logger.w("[$id] channel is closed");
return;
@@ -103,15 +111,16 @@ class Channel {
isWriting = true;
try {
var data = pipeline._encoder.encode(obj);
if (!isClosed) {
_socket.add(data);
_socket.add(bytes);
}
await _socket.flush();
} catch (e, t) {
// print(getAttribute(id)._attributes);
print(e);
print(t);
if (e is StateError && e.message == "StreamSink is closed") {
isOpen = false;
} else {
logger.e("[$id] write error", error: e, stackTrace: t);
}
} finally {
isWriting = false;
}
@@ -141,6 +150,37 @@ class Channel {
///返回此channel是否打开
bool get isClosed => !isOpen;
@override
String toString() {
return 'Channel($id ${remoteAddress.host}:$remotePort)';
}
}
///
class ChannelContext {
final Map<String, Object> _attributes = {};
//和本地客户端的连接
Channel? clientChannel;
//和远程服务端的连接
Channel? serverChannel;
EventListener? listener;
//http2 stream
final Map<int, Pair<HttpRequest, ValueWrap<HttpResponse>>> _streams = {};
ChannelContext();
//创建服务端连接
Future<Channel> connectServerChannel(HostAndPort hostAndPort, ChannelHandler channelHandler) async {
serverChannel = await HttpClients.startConnect(hostAndPort, channelHandler, this);
putAttribute(clientChannel!.id, serverChannel);
putAttribute(serverChannel!.id, clientChannel);
return serverChannel!;
}
T? getAttribute<T>(String key) {
if (!_attributes.containsKey(key)) {
return null;
@@ -156,9 +196,39 @@ class Channel {
_attributes[key] = value;
}
@override
String toString() {
return 'Channel($id ${remoteAddress.host}:$remotePort)';
HostAndPort? get host => getAttribute(AttributeKeys.host);
set host(HostAndPort? host) => putAttribute(AttributeKeys.host, host);
HttpRequest? get currentRequest => getAttribute(AttributeKeys.request);
set currentRequest(HttpRequest? request) => putAttribute(AttributeKeys.request, request);
StreamSetting? setting;
HttpRequest? putStreamRequest(int streamId, HttpRequest request) {
var old = _streams[streamId]?.key;
_streams[streamId] = Pair(request, ValueWrap());
return old;
}
void putStreamResponse(int streamId, HttpResponse response) {
var stream = _streams[streamId]!;
stream.key.response = response;
response.request = stream.key;
stream.value.set(response);
}
HttpRequest? getStreamRequest(int streamId) {
return _streams[streamId]?.key;
}
HttpResponse? getStreamResponse(int streamId) {
return _streams[streamId]?.value.get();
}
void removeStream(int streamId) {
_streams.remove(streamId);
}
}
@@ -166,7 +236,6 @@ class ChannelPipeline extends ChannelHandler<Uint8List> {
late Decoder _decoder;
late Encoder _encoder;
late ChannelHandler handler;
EventListener? listener;
final ByteBuf buffer = ByteBuf();
@@ -177,17 +246,16 @@ class ChannelPipeline extends ChannelHandler<Uint8List> {
}
/// 监听
void listen(Channel channel) {
void listen(Channel channel, ChannelContext channelContext) {
buffer.clear();
channel.socket.listen((data) => channel.pipeline.channelRead(channel, data),
onError: (error, trace) => channel.pipeline.exceptionCaught(channel, error, trace: trace),
onDone: () => channel.pipeline.channelInactive(channel));
channel.socket.listen((data) => channel.pipeline.channelRead(channelContext, channel, data),
onError: (error, trace) => channel.pipeline.exceptionCaught(channelContext, channel, error, trace: trace),
onDone: () => channel.pipeline.channelInactive(channelContext, channel));
}
@override
void channelActive(Channel channel) {
handler.channelActive(channel);
void channelActive(ChannelContext context, Channel channel) {
handler.channelActive(context, channel);
}
/// 转发请求
@@ -198,101 +266,117 @@ class ChannelPipeline extends ChannelHandler<Uint8List> {
}
///远程转发请求
remoteForward(Channel clientChannel, HostAndPort remote, Uint8List msg) async {
Channel? remoteChannel = clientChannel.getAttribute(clientChannel.id);
remoteChannel = remoteChannel ?? await HttpClients.startConnect(remote, RelayHandler(clientChannel));
remoteForward(ChannelContext channelContext, HostAndPort remote, Uint8List msg) async {
var clientChannel = channelContext.clientChannel!;
Channel? remoteChannel =
channelContext.serverChannel ?? await channelContext.connectServerChannel(remote, RelayHandler(clientChannel));
if (clientChannel.isSsl && !remoteChannel.isSsl) {
remoteChannel.secureSocket = await SecureSocket.secure(remoteChannel.socket,
host: clientChannel.getAttribute(AttributeKeys.domain), onBadCertificate: (certificate) => true);
SecureSocket secureSocket = await SecureSocket.secure(remoteChannel.socket,
host: channelContext.getAttribute(AttributeKeys.domain), onBadCertificate: (certificate) => true);
remoteChannel.secureSocket(secureSocket, channelContext);
}
relay(clientChannel, remoteChannel);
handler.channelRead(clientChannel, msg);
handler.channelRead(channelContext, clientChannel, msg);
}
@override
void channelRead(Channel channel, Uint8List msg) async {
void channelRead(ChannelContext channelContext, Channel channel, Uint8List msg) async {
try {
//手机扫码连接转发远程
HostAndPort? remote = channel.getAttribute(AttributeKeys.remote);
Channel? remoteChannel = channel.getAttribute(channel.id);
HostAndPort? remote = channelContext.getAttribute(AttributeKeys.remote);
if (remote != null) {
remoteForward(channel, remote, msg);
remoteForward(channelContext, remote, msg);
return;
}
buffer.add(msg);
Channel? remoteChannel = channelContext.getAttribute(channel.id);
//大body 不解析直接转发
if (buffer.length > Codec.maxBodyLength) {
relay(channel, remoteChannel!);
handler.channelRead(channel, buffer.buffer);
handler.channelRead(channelContext, channel, buffer.bytes);
buffer.clear();
return;
}
HttpRequest? request = remoteChannel?.getAttribute(AttributeKeys.request);
var data = _decoder.decode(buffer, resolveBody: request?.method != HttpMethod.head);
if (data == null) {
var decodeResult = _decoder.decode(channelContext, buffer);
if (!decodeResult.isDone) {
return;
}
if (decodeResult.forward != null) {
if (remoteChannel != null) {
await remoteChannel.writeBytes(decodeResult.forward!);
} else {
logger.w("[$channel] forward remoteChannel is null");
}
buffer.clearRead();
return;
}
var length = buffer.length;
buffer.clear();
buffer.clearRead();
var data = decodeResult.data;
if (data is HttpRequest) {
data.packageSize = length;
data.hostAndPort = channel.getAttribute(AttributeKeys.host) ?? getHostAndPort(data, ssl: channel.isSsl);
channelContext.currentRequest = data;
data.hostAndPort = channelContext.host ?? getHostAndPort(data, ssl: channel.isSsl);
if (data.headers.host != null && data.headers.host?.contains(":") == false) {
data.hostAndPort?.host = data.headers.host!;
}
}
if (data is HttpResponse) {
data.requestId = channelContext.currentRequest?.requestId ?? data.requestId;
data.packageSize = length;
data.remoteAddress = '${channel.remoteAddress.host}:${channel.remotePort}';
data.request = request;
request?.response = data;
data.request ??= channelContext.currentRequest;
channelContext.currentRequest?.response = data;
}
//websocket协议
if (data is HttpResponse && data.isWebSocket && remoteChannel != null) {
request?.hostAndPort?.scheme = channel.isSsl ? HostAndPort.wssScheme : HostAndPort.wsScheme;
channelContext.currentRequest?.hostAndPort?.scheme =
channel.isSsl ? HostAndPort.wssScheme : HostAndPort.wsScheme;
logger.d("webSocket ${data.request?.hostAndPort}");
remoteChannel.write(data);
var rawCodec = RawCodec();
channel.pipeline.handle(rawCodec, rawCodec, WebSocketChannelHandler(remoteChannel, data, listener: listener));
remoteChannel.pipeline
.handle(rawCodec, rawCodec, WebSocketChannelHandler(channel, data.request!, listener: listener));
channel.pipeline.handle(rawCodec, rawCodec, WebSocketChannelHandler(remoteChannel, data));
remoteChannel.pipeline.handle(rawCodec, rawCodec, WebSocketChannelHandler(channel, data.request!));
return;
}
handler.channelRead(channel, data!);
handler.channelRead(channelContext, channel, data!);
} catch (error, trace) {
buffer.clear();
exceptionCaught(channel, error, trace: trace);
exceptionCaught(channelContext, channel, error, trace: trace);
}
}
@override
exceptionCaught(Channel channel, dynamic error, {StackTrace? trace}) {
handler.exceptionCaught(channel, error, trace: trace);
exceptionCaught(ChannelContext channelContext, Channel channel, dynamic error, {StackTrace? trace}) {
handler.exceptionCaught(channelContext, channel, error, trace: trace);
}
@override
channelInactive(Channel channel) {
handler.channelInactive(channel);
channelInactive(ChannelContext channelContext, Channel channel) {
handler.channelInactive(channelContext, channel);
}
}
class RawCodec extends Codec<Object> {
class RawCodec extends Codec<dynamic> {
@override
Object? decode(ByteBuf data, {bool resolveBody = true}) {
return data.readBytes(data.readableBytes());
DecoderResult<dynamic> decode(ChannelContext channelContext, ByteBuf byteBuf, {bool resolveBody = true}) {
var decoderResult = DecoderResult()..data = byteBuf.readAvailableBytes();
return decoderResult;
}
@override
List<int> encode(Object data) {
List<int> encode(dynamic data) {
return data as List<int>;
}
}

View File

@@ -340,7 +340,7 @@ class RequestRewrites {
if (rewriteRule.type == RuleType.responseReplace) {
var rewriteItems = await getRewriteItems(rewriteRule);
rewriteItems.where((item) => item.enabled).forEach((item) => _replaceResponse(response, item));
logger.d('rewrite response $response');
// logger.d('rewrite response $response');
return;
}
@@ -359,6 +359,8 @@ class RequestRewrites {
}
return item.value ?? '';
}));
message.headers.contentLength = message.body!.length;
return;
}
@@ -408,6 +410,7 @@ class RequestRewrites {
if (item.body != null &&
(item.type == RewriteType.replaceResponseBody || item.type == RewriteType.replaceRequestBody)) {
message.body = utf8.encode(item.body!);
message.headers.contentLength = message.body!.length;
return;
}
}

View File

@@ -37,7 +37,7 @@ import 'http_client.dart';
abstract class EventListener {
void onRequest(Channel channel, HttpRequest request);
void onResponse(Channel channel, HttpResponse response);
void onResponse(ChannelContext channelContext, HttpResponse response);
void onMessage(Channel channel, HttpMessage message, WebSocketFrame frame) {}
}
@@ -50,8 +50,7 @@ class HttpProxyChannelHandler extends ChannelHandler<HttpRequest> {
HttpProxyChannelHandler({this.listener, this.requestRewrites});
@override
void channelRead(Channel channel, HttpRequest msg) async {
channel.putAttribute(AttributeKeys.request, msg);
void channelRead(ChannelContext channelContext, Channel channel, HttpRequest msg) async {
//下载证书
if (msg.uri == 'http://proxy.pin/ssl' || msg.requestUrl == 'http://127.0.0.1:${channel.socket.port}/ssl') {
ProxyHelper.crtDownload(channel, msg);
@@ -64,37 +63,36 @@ class HttpProxyChannelHandler extends ChannelHandler<HttpRequest> {
}
//代理转发请求
forward(channel, msg).catchError((error, trace) {
exceptionCaught(channel, error, trace: trace);
forward(channelContext, channel, msg).catchError((error, trace) {
exceptionCaught(channelContext, channel, error, trace: trace);
});
}
@override
void exceptionCaught(Channel channel, error, {StackTrace? trace}) {
super.exceptionCaught(channel, error, trace: trace);
ProxyHelper.exceptionHandler(channel, listener, channel.getAttribute(AttributeKeys.request), error);
void exceptionCaught(ChannelContext channelContext, Channel channel, error, {StackTrace? trace}) {
super.exceptionCaught(channelContext, channel, error, trace: trace);
ProxyHelper.exceptionHandler(channelContext, channel, listener, channelContext.currentRequest, error);
}
@override
void channelInactive(Channel channel) {
Channel? remoteChannel = channel.getAttribute(channel.id);
void channelInactive(ChannelContext channelContext, Channel channel) {
Channel? remoteChannel = channelContext.serverChannel;
remoteChannel?.close();
// log.i("[${channel.id}] close ${channel.error}");
// log.d("[${channel.id}] close ${channel.error}");
}
/// 转发请求
Future<void> forward(Channel channel, HttpRequest httpRequest) async {
// log.i("[${channel.id}] ${httpRequest.method.name} ${httpRequest.requestUrl}");
Future<void> forward(ChannelContext channelContext, Channel channel, HttpRequest httpRequest) async {
// log.d("[${channel.id}] ${httpRequest.method.name} ${httpRequest.requestUrl}");
if (channel.error != null) {
ProxyHelper.exceptionHandler(channel, listener, httpRequest, channel.error);
ProxyHelper.exceptionHandler(channelContext, channel, listener, httpRequest, channel.error);
return;
}
//获取远程连接
Channel remoteChannel;
try {
remoteChannel = await _getRemoteChannel(channel, httpRequest);
remoteChannel.putAttribute(remoteChannel.id, channel);
remoteChannel = await _getRemoteChannel(channelContext, channel, httpRequest);
} catch (error) {
channel.error = error; //记录异常
//https代理新建连接请求
@@ -131,7 +129,7 @@ class HttpProxyChannelHandler extends ChannelHandler<HttpRequest> {
var uri = '${httpRequest.remoteDomain()}${httpRequest.path()}';
String? redirectUrl = await requestRewrites?.getRedirectRule(uri);
if (redirectUrl?.isNotEmpty == true) {
await redirect(channel, httpRequest, redirectUrl!);
await redirect(channelContext, channel, httpRequest, redirectUrl!);
return;
}
@@ -140,43 +138,45 @@ class HttpProxyChannelHandler extends ChannelHandler<HttpRequest> {
}
//重定向
Future<void> redirect(Channel channel, HttpRequest httpRequest, String redirectUrl) async {
Future<void> redirect(
ChannelContext channelContext, Channel channel, HttpRequest httpRequest, String redirectUrl) async {
var proxyHandler = HttpResponseProxyHandler(channel, listener: listener, requestRewrites: requestRewrites);
var redirectUri = UriBuild.build(redirectUrl, params: httpRequest.queries);
httpRequest.uri = redirectUri.toString();
httpRequest.headers.host = redirectUri.host;
var redirectChannel = await HttpClients.connect(Uri.parse(redirectUrl), proxyHandler);
var redirectChannel = await HttpClients.connect(Uri.parse(redirectUrl), proxyHandler, channelContext);
channelContext.serverChannel = redirectChannel;
await redirectChannel.write(httpRequest);
}
/// 获取远程连接
Future<Channel> _getRemoteChannel(Channel clientChannel, HttpRequest httpRequest) async {
String clientId = clientChannel.id;
Future<Channel> _getRemoteChannel(
ChannelContext channelContext, Channel clientChannel, HttpRequest httpRequest) async {
//客户端连接 作为缓存
Channel? remoteChannel = clientChannel.getAttribute(clientId);
Channel? remoteChannel = channelContext.serverChannel;
if (remoteChannel != null) {
return remoteChannel;
}
var hostAndPort = httpRequest.hostAndPort ?? getHostAndPort(httpRequest);
clientChannel.putAttribute(AttributeKeys.host, hostAndPort);
channelContext.host = hostAndPort;
//远程转发
HostAndPort? remote = clientChannel.getAttribute(AttributeKeys.remote);
HostAndPort? remote = channelContext.getAttribute(AttributeKeys.remote);
//外部代理
ProxyInfo? proxyInfo = clientChannel.getAttribute(AttributeKeys.proxyInfo);
ProxyInfo? proxyInfo = channelContext.getAttribute(AttributeKeys.proxyInfo);
if (remote != null || proxyInfo != null) {
HostAndPort connectHost = remote ?? HostAndPort.host(proxyInfo!.host, proxyInfo.port!);
var proxyChannel = await connectRemote(clientChannel, connectHost);
var proxyChannel = await connectRemote(channelContext, clientChannel, connectHost);
if (httpRequest.method == HttpMethod.connect) {
proxyChannel.write(httpRequest);
}
return proxyChannel;
}
var proxyChannel = await connectRemote(clientChannel, hostAndPort);
var proxyChannel = await connectRemote(channelContext, clientChannel, hostAndPort);
//https代理新建连接请求
if (httpRequest.method == HttpMethod.connect) {
await clientChannel.write(
@@ -186,16 +186,14 @@ class HttpProxyChannelHandler extends ChannelHandler<HttpRequest> {
}
/// 连接远程
Future<Channel> connectRemote(Channel clientChannel, HostAndPort connectHost) async {
Future<Channel> connectRemote(ChannelContext channelContext, Channel clientChannel, HostAndPort connectHost) async {
var proxyHandler = HttpResponseProxyHandler(clientChannel, listener: listener, requestRewrites: requestRewrites);
var proxyChannel = await HttpClients.startConnect(connectHost, proxyHandler);
proxyChannel.pipeline.listener = listener;
String clientId = clientChannel.id;
clientChannel.putAttribute(clientId, proxyChannel);
var proxyChannel = await channelContext.connectServerChannel(connectHost, proxyHandler);
if (clientChannel.isSsl) {
proxyChannel.secureSocket = await SecureSocket.secure(proxyChannel.socket,
SecureSocket secureSocket = await SecureSocket.secure(proxyChannel.socket,
host: connectHost.host, onBadCertificate: (certificate) => true);
proxyChannel.secureSocket(secureSocket, channelContext);
}
return proxyChannel;
}
@@ -212,9 +210,11 @@ class HttpResponseProxyHandler extends ChannelHandler<HttpResponse> {
HttpResponseProxyHandler(this.clientChannel, {this.listener, this.requestRewrites});
@override
void channelRead(Channel channel, HttpResponse msg) async {
void channelRead(ChannelContext channelContext, Channel channel, HttpResponse msg) async {
var request = channelContext.currentRequest;
//域名是否过滤
if (HostFilter.filter(msg.request?.hostAndPort?.host) || msg.request?.method == HttpMethod.connect) {
if (HostFilter.filter(request?.hostAndPort?.host) || request?.method == HttpMethod.connect) {
await clientChannel.write(msg);
return;
}
@@ -237,13 +237,13 @@ class HttpResponseProxyHandler extends ChannelHandler<HttpResponse> {
//重写响应
await requestRewrites?.responseRewrite(msg.request?.requestUrl, msg);
listener?.onResponse(clientChannel, msg);
listener?.onResponse(channelContext, msg);
//发送给客户端
await clientChannel.write(msg);
}
@override
void channelInactive(Channel channel) {
void channelInactive(ChannelContext channelContext, Channel channel) {
clientChannel.close();
}
}
@@ -254,29 +254,28 @@ class RelayHandler extends ChannelHandler<Object> {
RelayHandler(this.remoteChannel);
@override
void channelRead(Channel channel, Object msg) async {
void channelRead(ChannelContext channelContext, Channel channel, Object msg) async {
//发送给客户端
remoteChannel.write(msg);
}
@override
void channelInactive(Channel channel) {
void channelInactive(ChannelContext channelContext, Channel channel) {
remoteChannel.close();
}
}
//
/// websocket处理器
class WebSocketChannelHandler extends ChannelHandler<Uint8List> {
final WebSocketDecoder decoder = WebSocketDecoder();
final Channel proxyChannel;
final HttpMessage message;
EventListener? listener;
WebSocketChannelHandler(this.proxyChannel, this.message, {this.listener});
WebSocketChannelHandler(this.proxyChannel, this.message);
@override
void channelRead(Channel channel, Uint8List msg) {
void channelRead(ChannelContext channelContext, Channel channel, Uint8List msg) {
proxyChannel.write(msg);
var frame = decoder.decode(msg);
@@ -286,7 +285,7 @@ class WebSocketChannelHandler extends ChannelHandler<Uint8List> {
frame.isFromClient = message is HttpRequest;
message.messages.add(frame);
listener?.onMessage(channel, message, frame);
channelContext.listener?.onMessage(channel, message, frame);
logger.d("socket channelRead ${frame.payloadLength} ${frame.fin} ${frame.payloadDataAsString}");
}
}

View File

@@ -17,6 +17,7 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:network_proxy/network/http/constants.dart';
import 'package:network_proxy/network/http/http.dart';
import '../../utils/num.dart';

View File

@@ -14,29 +14,21 @@
* limitations under the License.
*/
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:network_proxy/network/channel.dart';
import 'package:network_proxy/network/http/body_reader.dart';
import 'package:network_proxy/network/http/constants.dart';
import 'package:network_proxy/network/http/h2/codec.dart';
import 'package:network_proxy/network/http/http_parser.dart';
import 'package:network_proxy/network/util/byte_buf.dart';
import '../../utils/compress.dart';
import 'http.dart';
import 'http_headers.dart';
class HttpConstants {
/// Line feed character /n
static const int lf = 10;
/// Carriage return /r
static const int cr = 13;
/// Horizontal space
static const int sp = 32;
/// Colon ':'
static const int colon = 58;
}
class ParserException implements Exception {
final String message;
@@ -57,63 +49,20 @@ enum State {
done,
}
///类似于netty ByteBuf
class ByteBuf {
final BytesBuilder _buffer = BytesBuilder();
class DecoderResult<T> {
bool isDone = true;
T? data;
int _readerIndex = 0;
//转发消息
List<int>? forward;
Uint8List get buffer => _buffer.toBytes();
int get length => _buffer.length;
///添加
void add(List<int> bytes) {
_buffer.add(bytes);
}
///清空
clear() {
_buffer.clear();
_readerIndex = 0;
}
///读取索引
int get readerIndex => _readerIndex;
bool isReadable() => _readerIndex < _buffer.length;
///可读字节数
int readableBytes() {
return _buffer.length - _readerIndex;
}
///读取字节
Uint8List readBytes(int length) {
Uint8List bytes = buffer.sublist(_readerIndex, _readerIndex + length);
_readerIndex += length;
return bytes;
}
///跳过
skipBytes(int length) {
_readerIndex += length;
}
///读取字节
int read() {
return buffer[_readerIndex++];
}
int get(int index) {
return buffer[index];
}
DecoderResult({this.isDone = true});
}
/// 解码
abstract interface class Decoder<T> {
/// 解码 如果返回null说明数据不完整
T? decode(ByteBuf byteBuf, {bool resolveBody = true});
DecoderResult<T> decode(ChannelContext channelContext, ByteBuf byteBuf);
}
/// 编码
@@ -130,60 +79,76 @@ abstract class Codec<T> implements Decoder<T>, Encoder<T> {
/// http编解码
abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
final HttpParse _httpParse = HttpParse();
Http2Codec<T>? _h2Codec;
State _state = State.readInitial;
late T message;
late DecoderResult<T> result;
BodyReader? bodyReader;
T createMessage(List<String> reqLine);
Http2Codec<T> getH2Codec() {
return _h2Codec ??= (this is HttpRequestCodec ? Http2RequestDecoder() : Http2ResponseDecoder()) as Http2Codec<T>;
}
@override
T? decode(ByteBuf data, {bool resolveBody = true}) {
DecoderResult<T> decode(ChannelContext channelContext, ByteBuf data) {
if (channelContext.serverChannel?.selectedProtocol == HttpConstants.h2) {
return getH2Codec().decode(channelContext, data);
}
//请求行
if (_state == State.readInitial) {
init();
var initialLine = _readInitialLine(data);
message = createMessage(initialLine);
result.data = createMessage(initialLine);
_state = State.readHeader;
}
//请求头
try {
if (_state == State.readHeader) {
_readHeader(data, message);
_readHeader(data, result.data!);
}
//请求体
if (_state == State.body) {
var result = resolveBody ? bodyReader!.readBody(data.readBytes(data.readableBytes())) : null;
if (!resolveBody || result?.isDone == true) {
bool resolveBody = channelContext.currentRequest?.method != HttpMethod.head;
var bodyResult = resolveBody ? bodyReader!.readBody(data.readAvailableBytes()) : null;
if (!resolveBody || bodyResult?.isDone == true) {
_state = State.done;
message.body = result?.body;
result.data!.body = bodyResult?.body;
}
}
if (_state == State.done) {
message.body = _convertBody(message.body);
result.data!.body = _convertBody(result.data!.body);
_state = State.readInitial;
return message;
result.isDone = true;
return result;
}
} catch (e) {
_state = State.readInitial;
rethrow;
}
return null;
return result;
}
void init() {
bodyReader = null;
result = DecoderResult(isDone: false);
}
void initialLine(BytesBuilder buffer, T message);
@override
List<int> encode(T message) {
if (message.streamId != null) {
return getH2Codec().encode(message);
}
BytesBuilder builder = BytesBuilder();
//请求行
initialLine(builder, message);
@@ -195,10 +160,13 @@ abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
//请求头
message.headers.remove(HttpHeaders.TRANSFER_ENCODING);
message.headers.remove(HttpHeaders.CONTENT_LENGTH);
if (body != null && body.isNotEmpty) {
message.headers.contentLength = body.length;
} else if (message.contentLength != 0){
message.headers.remove(HttpHeaders.CONTENT_LENGTH);
}
message.headers.forEach((key, values) {
for (var v in values) {
builder
@@ -227,7 +195,6 @@ abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
//读取请求头
void _readHeader(ByteBuf data, T message) {
if (_httpParse.parseHeaders(data, message.headers)) {
message.contentLength = message.headers.contentLength;
_state = State.body;
bodyReader = BodyReader(message);
}
@@ -238,7 +205,7 @@ abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
if (bytes == null) {
return null;
}
if (message.headers.isGzip) {
if (result.data!.headers.isGzip) {
bytes = gzipDecode(bytes);
}
return bytes;

View File

@@ -0,0 +1,16 @@
class HttpConstants {
//h2协议
static const String h2 = 'h2';
/// Line feed character /n
static const int lf = 10;
/// Carriage return /r
static const int cr = 13;
/// Horizontal space
static const int sp = 32;
/// Colon ':'
static const int colon = 58;
}

View File

@@ -0,0 +1,360 @@
/*
* Copyright 2023 the original author or authors.
*
* 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:math';
import 'dart:typed_data';
import 'package:network_proxy/network/channel.dart';
import 'package:network_proxy/network/http/codec.dart';
import 'package:network_proxy/network/http/h2/hpack.dart';
import 'package:network_proxy/network/http/h2/setting.dart';
import 'package:network_proxy/network/http/http.dart';
import 'package:network_proxy/network/util/byte_buf.dart';
import 'frame.dart';
/// http编解码
abstract class Http2Codec<T extends HttpMessage> implements Codec<T> {
static const maxFrameSize = 16384;
static final List<int> connectionPrefacePRI = "PRI * HTTP/2.0".codeUnits;
HPACKDecoder decoder = HPACKDecoder();
HPACKEncoder encoder = HPACKEncoder();
T createMessage(ChannelContext channelContext, FrameHeader frameHeader, Map<String, String> headers);
T? getMessage(ChannelContext channelContext, FrameHeader frameHeader);
@override
DecoderResult<T> decode(ChannelContext channelContext, ByteBuf byteBuf, {bool resolveBody = true}) {
//Connection Preface PRI * HTTP/2.0
if (byteBuf.get(byteBuf.readerIndex) == 0x50 &&
byteBuf.get(byteBuf.readerIndex + 1) == 0x52 &&
byteBuf.get(byteBuf.readerIndex + 2) == 0x49 &&
isConnectionPrefacePRI(byteBuf)) {
DecoderResult<T> result = DecoderResult<T>();
result.forward = byteBuf.readAvailableBytes();
return result;
}
while (byteBuf.isReadable()) {
DecoderResult<T> result = DecoderResult<T>(isDone: false);
FrameHeader? frameHeader = FrameReader._readFrameHeader(byteBuf);
if (frameHeader == null) {
return result;
}
List<int>? framePayload = FrameReader._readFramePayload(byteBuf, frameHeader.length);
if (framePayload == null) {
byteBuf.readerIndex -= FrameReader.headerLength;
return result;
}
result = parseHttp2Packet(channelContext, frameHeader, ByteBuf(framePayload));
if (result.isDone) {
return result;
}
}
return DecoderResult<T>(isDone: false);
}
DecoderResult<T> parseHttp2Packet(ChannelContext channelContext, FrameHeader frameHeader, ByteBuf framePayload) {
var result = DecoderResult<T>();
// logger.d("streamId: ${frameHeader.streamIdentifier} ${frameHeader.type} endHeaders: ${frameHeader.hasEndHeadersFlag} "
// "endStream: ${frameHeader.hasEndStreamFlag}");
//根据帧类型进行处理
switch (frameHeader.type) {
case FrameType.headers:
//处理HEADERS帧
_handleHeadersFrame(channelContext, frameHeader, framePayload);
result.isDone = frameHeader.hasEndStreamFlag && frameHeader.hasEndHeadersFlag;
break;
case FrameType.continuation:
//处理CONTINUATION帧
var message = getMessage(channelContext, frameHeader);
if (message == null) {
result.forward = List.from(frameHeader.encode())..addAll(framePayload.readAvailableBytes());
return result;
}
Map<String, String> headers = _parseHeaders(channelContext, framePayload.readBytes(frameHeader.length));
headers.forEach((key, value) => message.headers.add(key, value));
if (frameHeader.hasEndHeadersFlag &&
channelContext.getStreamRequest(frameHeader.streamIdentifier)?.method == HttpMethod.head) {
result.isDone = true;
}
break;
case FrameType.data:
//处理DATA帧
_handleDataFrame(channelContext, frameHeader, framePayload);
result.isDone = frameHeader.hasEndStreamFlag;
break;
case FrameType.settings:
SettingHandler.handleSettingsFrame(channelContext, frameHeader, framePayload);
result.forward = List.from(frameHeader.encode())..addAll(framePayload.bytes);
return result;
default:
//其他帧类型 原文转发
result.forward = List.from(frameHeader.encode())..addAll(framePayload.bytes);
return result;
}
if (result.isDone && frameHeader.streamIdentifier > 0) {
result.data = getMessage(channelContext, frameHeader);
result.data?.streamId = frameHeader.streamIdentifier;
channelContext.currentRequest = channelContext.getStreamRequest(frameHeader.streamIdentifier);
if (result.data is HttpResponse) {
channelContext.removeStream(frameHeader.streamIdentifier);
}
}
return result;
}
List<Header> encodeHeaders(T message);
@override
Uint8List encode(T data) {
var bytesBuilder = BytesBuilder();
//headers
var headers = encodeHeaders(data);
BytesBuilder headerBlock = BytesBuilder();
bool firstFrame = true;
for (var header in headers) {
var encode = encoder.encode(header);
//防止出现桢分片导致header分裂
if (headerBlock.length + encode.length < maxFrameSize) {
headerBlock.add(encode);
continue;
}
FrameType frameType = firstFrame ? FrameType.headers : FrameType.continuation;
int flags = frameType == FrameType.headers && data.body == null ? FrameHeader.flagsEndStream : 0;
firstFrame = false;
_writeFrame(bytesBuilder, frameType, flags, data.streamId!, headerBlock.takeBytes());
headerBlock.add(encode);
}
FrameType frameType = firstFrame ? FrameType.headers : FrameType.continuation;
int flags = frameType == FrameType.headers && data.body == null ? FrameHeader.flagsEndStream : 0;
flags |= FrameHeader.flagsEndHeaders;
_writeFrame(bytesBuilder, frameType, flags, data.streamId!, headerBlock.takeBytes());
//body
if (data.body != null) {
var payload = data.body!;
while (payload.length > maxFrameSize) {
var chunkSize = min(maxFrameSize, payload.length);
var chunk = payload.sublist(0, chunkSize);
payload = payload.sublist(chunkSize);
_writeFrame(bytesBuilder, FrameType.data, 0, data.streamId!, chunk);
}
_writeFrame(bytesBuilder, FrameType.data, FrameHeader.flagsEndStream, data.streamId!, payload);
}
return bytesBuilder.takeBytes();
}
void _writeFrame(BytesBuilder bytesBuilder, FrameType type, int flag, int streamId, List<int> payload) {
FrameHeader frameHeader = FrameHeader(payload.length, type, flag, streamId);
// logger.d("_writeFrame streamId: ${frameHeader.streamIdentifier} ${frameHeader.type} endHeaders: ${frameHeader
// .hasEndHeadersFlag} endStream: ${frameHeader.hasEndStreamFlag}");
bytesBuilder.add(frameHeader.encode());
bytesBuilder.add(payload);
}
bool isConnectionPrefacePRI(ByteBuf data) {
if (data.readableBytes() < 9) {
return false;
}
for (int i = 0; i < connectionPrefacePRI.length; i++) {
if (data.get(data.readerIndex + i) != connectionPrefacePRI[i]) {
return false;
}
}
return true;
}
DataFrame _handleDataFrame(ChannelContext channelContext, FrameHeader frameHeader, ByteBuf payload) {
// DATA 帧格式
int padLength = 0;
//如果帧头部有PADDED标志位则需要读取PADDED长度
if (frameHeader.hasPaddedFlag) {
padLength = payload.readByte();
}
frameHeader.length;
int dataLength = payload.readableBytes() - padLength;
var data = payload.readBytes(dataLength);
var message = getMessage(channelContext, frameHeader)!;
if (message.body == null) {
message.body = data;
} else {
message.body = List.from(message.body!)..addAll(data);
}
// print("DataFrame ${message.bodyAsString}");
return DataFrame(frameHeader, padLength, data);
}
HeadersFrame _handleHeadersFrame(ChannelContext channelContext, FrameHeader frameHeader, ByteBuf payload) {
// HEADERS 帧格式
int padLength = 0;
//如果帧头部有PADDED标志位则需要读取PADDED长度
if (frameHeader.hasPaddedFlag) {
padLength = payload.readByte();
}
int? streamDependency;
bool exclusiveDependency = false;
int? weight;
//如果帧头部有PRIORITY标志位则需要读取优先级信息
if (frameHeader.hasPriorityFlag) {
//读取优先级信息
int dependency = payload.readInt();
exclusiveDependency = (dependency & 0x80000000) == 0x80000000;
streamDependency = dependency & 0x7fffffff;
weight = payload.readByte(); // weight
}
var headerBlockLength = payload.length - payload.readerIndex - padLength;
if (headerBlockLength < 0) {
throw Exception("headerBlockLength < 0");
}
var blockFragment = payload.readBytes(headerBlockLength);
//读取头部信息
Map<String, String> headers = _parseHeaders(channelContext, blockFragment);
T message = createMessage(channelContext, frameHeader, headers);
headers.forEach((key, value) {
if (!key.startsWith(":")) {
message.headers.add(key, value);
}
});
return HeadersFrame(frameHeader, padLength, exclusiveDependency, streamDependency, weight, blockFragment);
}
Map<String, String> _parseHeaders(ChannelContext channelContext, List<int> payload) {
if (channelContext.setting != null) {
decoder.updateTableSize(channelContext.setting!.headTableSize);
}
// Decode the headers
List<Header> headers = decoder.decode(payload);
// Convert the headers to a map
Map<String, String> headerMap = {};
for (Header header in headers) {
headerMap[header.name] = header.value;
}
return headerMap;
}
}
class Http2RequestDecoder extends Http2Codec<HttpRequest> {
@override
HttpRequest createMessage(ChannelContext channelContext, FrameHeader frameHeader, Map<String, String> headers) {
HttpMethod httpMethod = HttpMethod.valueOf(headers[":method"]!);
var httpRequest = HttpRequest(httpMethod, headers[":path"]!, protocolVersion: headers[":version"] ?? "HTTP/2");
var old = channelContext.putStreamRequest(frameHeader.streamIdentifier, httpRequest);
assert(old == null, "old request is not null");
return httpRequest;
}
@override
HttpRequest? getMessage(ChannelContext channelContext, FrameHeader frameHeader) {
return channelContext.getStreamRequest(frameHeader.streamIdentifier);
}
@override
List<Header> encodeHeaders(HttpRequest message) {
var headers = <Header>[];
var uri = message.requestUri!;
headers.add(Header(":method", message.method.name));
headers.add(Header(":scheme", uri.scheme));
headers.add(Header(":authority", uri.host));
headers.add(Header(":path", message.uri));
message.headers.forEach((key, values) {
for (var value in values) {
headers.add(Header(key, value));
}
});
return headers;
}
}
class Http2ResponseDecoder extends Http2Codec<HttpResponse> {
@override
HttpResponse createMessage(ChannelContext channelContext, FrameHeader frameHeader, Map<String, String> headers) {
var httpResponse = HttpResponse(HttpStatus.valueOf(int.parse(headers[':status']!)),
protocolVersion: headers[":version"] ?? 'HTTP/2');
httpResponse.requestId = channelContext.getStreamRequest(frameHeader.streamIdentifier)!.requestId;
channelContext.putStreamResponse(frameHeader.streamIdentifier, httpResponse);
return httpResponse;
}
@override
HttpResponse? getMessage(ChannelContext channelContext, FrameHeader frameHeader) {
return channelContext.getStreamResponse(frameHeader.streamIdentifier);
}
@override
List<Header> encodeHeaders(HttpResponse message) {
var headers = <Header>[];
headers.add(Header(":status", message.status.code.toString()));
message.headers.forEach((key, values) {
for (var value in values) {
headers.add(Header(key, value));
}
});
return headers;
}
}
class FrameReader {
static int headerLength = 9;
static List<int>? _readFramePayload(ByteBuf data, int length) {
if (data.readableBytes() < length) {
return null;
}
return data.readBytes(length);
}
static FrameHeader? _readFrameHeader(ByteBuf data) {
if (data.readableBytes() < headerLength) {
return null;
}
int length = data.read() << 16 | data.read() << 8 | data.read();
FrameType type = FrameType.values[data.read()];
int flags = data.read();
int streamIdentifier = data.readInt();
return FrameHeader(length, type, flags, streamIdentifier);
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2023 the original author or authors.
*
* 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.
*/
enum FrameType { data, headers, priority, rstStream, settings, pushPromise, ping, goaway, windowUpdate, continuation }
class FrameHeader {
static const flagsEndStream = 0x01;
static const flagsEndHeaders = 0x04;
final int length;
final FrameType type;
int flags; // 8 bits
final int streamIdentifier;
FrameHeader(this.length, this.type, this.flags, this.streamIdentifier);
bool get hasPaddedFlag => (flags & 0x08) == 0x08;
bool get hasPriorityFlag => (flags & 0x20) == 0x20;
bool get hasEndHeadersFlag => (flags & flagsEndHeaders) == flagsEndHeaders;
bool get hasEndStreamFlag => (flags & flagsEndStream) == flagsEndStream;
bool get hasAckFlag => (flags & 0x01) == 0x01;
List<int> encode() {
var result = <int>[];
result.addAll(_intToBytes(length, 3)); // length is 24 bits
result.add(type.index); // type is 8 bits
result.add(flags); // flags is 8 bits
result.addAll(_intToBytes(streamIdentifier, 4)); // streamIdentifier is 32 bits
return result;
}
List<int> _intToBytes(int value, int byteCount) {
var bytes = <int>[];
for (var i = 0; i < byteCount; i++) {
bytes.insert(0, value & 0xff);
value >>= 8;
}
return bytes;
}
}
class Frame {
final FrameHeader header;
Frame(this.header);
Map toJson() => {
'length': header.length,
'type': header.type.toString().split('.')[1],
'flags': header.flags,
'streamIdentifier': header.streamIdentifier
};
}
class HeadersFrame extends Frame {
final int padLength;
final bool exclusiveDependency;
final int? streamDependency;
final int? weight;
final List<int> headerBlockFragment;
HeadersFrame(super.header, this.padLength, this.exclusiveDependency, this.streamDependency, this.weight,
this.headerBlockFragment);
}
class DataFrame extends Frame {
final int padLength;
final List<int> data;
DataFrame(super.header, this.padLength, this.data);
}

View File

@@ -0,0 +1,305 @@
/*
* Copyright 2023 the original author or authors.
*
* 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:convert';
import 'dart:typed_data';
import 'package:network_proxy/network/http/h2/huffman.dart';
import 'package:network_proxy/network/util/byte_buf.dart';
class HPACKDecoder {
// static const int _maxHeaderTableSize = 12288;
static HuffmanDecoder huffmanDecoder = HuffmanDecoder();
final IndexTable _indexTable = IndexTable();
updateTableSize(int size) {
_indexTable._maxSize = size;
}
List<Header> decode(List<int> bytes) {
var headers = <Header>[];
var payload = ByteBuf(bytes);
while (payload.isReadable()) {
var header = _decodeHeader(payload);
if (header == null) continue;
headers.add(header);
}
return headers;
}
Header? _decodeHeader(ByteBuf framePayload) {
if (!framePayload.isReadable()) {
return null;
}
int firstByte = framePayload.get(framePayload.readerIndex);
if (firstByte & 0x80 == 0x80) {
// Indexed Header Field
int headerIndex = _decodeInteger(framePayload, 7);
return _indexTable[headerIndex];
} else if (firstByte & 0x40 == 0x40) {
// Literal Header Field with Incremental Indexing
int headerIndex = _decodeInteger(framePayload, 6);
Header header = _readHeaderField(framePayload, headerIndex);
_indexTable.add(header);
return header;
} else if (firstByte & 0x20 == 0x20) {
// Dynamic Table Size Update
int maxSize = _decodeInteger(framePayload, 5);
_indexTable._maxSize = maxSize;
} else if (firstByte & 0x10 == 0x10) {
// Literal Header Field never Indexed
int headerIndex = _decodeInteger(framePayload, 4);
Header header = _readHeaderField(framePayload, headerIndex, neverIndexed: true);
return header;
} else {
// Literal Header Field without Indexing
int headerIndex = _decodeInteger(framePayload, 4);
Header header = _readHeaderField(framePayload, headerIndex);
return header;
}
return null;
}
Header _readHeaderField(ByteBuf data, int index, {bool neverIndexed = false}) {
if (index > 0) {
String name = _indexTable[index].name;
String value = _decodeString(data, neverIndexed: neverIndexed);
return Header(name, value);
}
String name = _decodeString(data);
String value = _decodeString(data, neverIndexed: neverIndexed);
return Header(name, value);
}
String _decodeString(ByteBuf data, {bool neverIndexed = false}) {
int firstByte = data.get(data.readerIndex);
bool huffmanEncoded = (firstByte & 0x80) == 0x80;
int length = _decodeInteger(data, 7);
Uint8List stringBytes = data.readBytes(length);
if (huffmanEncoded) {
// If the string is Huffman encoded, decode it using a Huffman decoder.
return ascii.decode(huffmanDecoder.decode(stringBytes));
} else {
// If the string is not Huffman encoded, simply create a new string from the bytes.
return ascii.decode(stringBytes);
}
}
int _decodeInteger(ByteBuf data, int prefixLength) {
int prefixMask = (1 << prefixLength) - 1;
int value = data.read() & prefixMask;
if (value < prefixMask) {
return value;
}
int shift = 0;
int b;
do {
b = data.read();
value += (b & 127) << shift;
shift += 7;
} while ((b & 128) == 128);
return value;
}
}
class HPACKEncoder {
final IndexTable _indexTable = IndexTable();
static HuffmanEncoder huffmanEncoder = HuffmanEncoder();
List<int> encodeList(List<Header> headers) {
var bytesBuilder = BytesBuilder();
for (var header in headers) {
_encodeHeader(bytesBuilder, header);
}
return bytesBuilder.takeBytes();
}
List<int> encode(Header header) {
var bytesBuilder = BytesBuilder();
_encodeHeader(bytesBuilder, header);
return bytesBuilder.takeBytes();
}
void _encodeHeader(BytesBuilder bytesBuilder, Header header) {
var name = header.name;
var value = header.value;
var index = _getIndex(name, value);
if (index != null) {
_encodeInteger(bytesBuilder, 7, 0x80, index);
return;
}
var nameIndex = _getIndex(name);
if (nameIndex != null) {
_encodeInteger(bytesBuilder, 4, 0, nameIndex);
} else {
bytesBuilder.addByte(0);
_encodeString(bytesBuilder, name);
}
_encodeString(bytesBuilder, value);
}
int? _getIndex(String name, [String? value]) {
for (var i = 1; i < _indexTable.length; i++) {
var header = _indexTable[i];
if (header.name == name && (value == null || header.value == value)) {
return i;
}
}
return null;
}
_encodeString(BytesBuilder bytesBuilder, String value) {
var encoded = ascii.encode(value);
var huffmanEncoded = huffmanEncoder.encode(encoded);
if (huffmanEncoded.length < encoded.length) {
_encodeInteger(bytesBuilder, 7, 0x80, huffmanEncoded.length);
bytesBuilder.add(huffmanEncoded);
} else {
_encodeInteger(bytesBuilder, 7, 0x00, encoded.length);
bytesBuilder.add(encoded);
}
}
void _encodeInteger(BytesBuilder bytesBuilder, int prefixBits, int mask, int value) {
assert(prefixBits <= 8);
if (value < (1 << prefixBits) - 1) {
bytesBuilder.addByte(value | mask);
} else {
bytesBuilder.addByte(((1 << prefixBits) - 1) | mask);
value -= (1 << prefixBits) - 1;
while (value >= 128) {
bytesBuilder.addByte(value % 128 + 128);
value ~/= 128;
}
bytesBuilder.addByte(value);
}
}
}
class Header {
final String name;
String value;
Header(String name, this.value) : name = name.toLowerCase();
@override
String toString() {
return '{name: $name, value: $value}';
}
}
class IndexTable {
static final List<Header> _staticTable = [
Header('', ''),
Header(':authority', ''),
Header(':method', 'GET'),
Header(':method', 'POST'),
Header(':path', '/'),
Header(':path', '/index.html'),
Header(':scheme', 'http'),
Header(':scheme', 'https'),
Header(':status', '200'),
Header(':status', '204'),
Header(':status', '206'),
Header(':status', '304'),
Header(':status', '400'),
Header(':status', '404'),
Header(':status', '500'),
Header('accept-charset', ''),
Header('accept-encoding', 'gzip, deflate, br'),
Header('accept-language', ''),
Header('accept-ranges', ''),
Header('accept', ''),
Header('access-control-allow-origin', ''),
Header('age', ''),
Header('allow', ''),
Header('authorization', ''),
Header('cache-control', ''),
Header('content-disposition', ''),
Header('content-encoding', ''),
Header('content-language', ''),
Header('content-length', ''),
Header('content-location', ''),
Header('content-range', ''),
Header('content-type', ''),
Header('cookie', ''),
Header('date', ''),
Header('etag', ''),
Header('expect', ''),
Header('expires', ''),
Header('from', ''),
Header('host', ''),
Header('if-match', ''),
Header('if-modified-since', ''),
Header('if-none-match', ''),
Header('if-range', ''),
Header('if-unmodified-since', ''),
Header('last-modified', ''),
Header('link', ''),
Header('location', ''),
Header('max-forwards', ''),
Header('proxy-authenticate', ''),
Header('proxy-authorization', ''),
Header('range', ''),
Header('referer', ''),
Header('refresh', ''),
Header('retry-after', ''),
Header('server', ''),
Header('set-cookie', ''),
Header('strict-transport-security', ''),
Header('transfer-encoding', ''),
Header('user-agent', ''),
Header('vary', ''),
Header('via', ''),
Header('www-authenticate', ''),
];
//动态表
final List<Header> _dynamicTable = [];
int _maxSize = 4096;
Header operator [](int index) {
if (index < _staticTable.length) return _staticTable[index];
if (index < _staticTable.length + _dynamicTable.length) {
return _dynamicTable[index - _staticTable.length];
}
throw RangeError('Invalid index: $index');
}
add(Header header) {
_dynamicTable.add(header);
_maxSize -= sizeOf(header);
while (_maxSize < 0) {
_maxSize += sizeOf(_dynamicTable.removeAt(0));
}
}
int get length => _staticTable.length + _dynamicTable.length;
int sizeOf(Header header) => header.name.length + header.value.length + 32;
}

View File

@@ -0,0 +1,417 @@
import 'dart:typed_data';
main() {
var list = [241, 227, 194, 254, 231, 52, 246, 174, 67, 211];
print(HuffmanDecoder().decode(list));
}
class HuffmanDecoder {
static const int eosByte = 256; //end of string
final HuffmanTreeNode _root;
HuffmanDecoder() : _root = generateHuffmanTree(_huffmanTable);
//http2协议规范 huffman解码
List<int> decode(List<int> bytes) {
var buffer = BytesBuilder();
var currentByteOffset = 0;
var node = _root;
var currentDepth = 0;
while (currentByteOffset < bytes.length) {
var byte = bytes[currentByteOffset];
for (var currentBit = 7; currentBit >= 0; currentBit--) {
var right = (byte >> currentBit) & 1 == 1;
if (right) {
node = node.right!;
} else {
node = node.left!;
}
currentDepth++;
if (node.value != null) {
if (node.value == eosByte) {
throw Exception('More than 7 bit padding is not allowed. Found entire EOS '
'encoding');
}
buffer.addByte(node.value!);
node = _root;
currentDepth = 0;
}
}
currentByteOffset++;
}
if (node != _root) {
if (currentDepth > 7) {
throw Exception('Incomplete encoding of a byte or more than 7 bit padding.');
}
while (node.right != null) {
node = node.right!;
}
if (node.value != 256) {
throw Exception('Incomplete encoding of a byte.');
}
}
return buffer.takeBytes();
}
}
class HuffmanEncoder {
static const int eosByte = 256; //end of string
final List<EncodedHuffmanValue> _codewords;
HuffmanEncoder() : _codewords = _huffmanTable;
//http2协议规范 huffman编码
List<int> encode(List<int> bytes) {
var buffer = BytesBuilder();
var currentByte = 0;
var currentBitOffset = 7;
void writeValue(int value, int numBits) {
var i = numBits - 1;
while (i >= 0) {
if (currentBitOffset == 7 && i >= 7) {
assert(currentByte == 0);
buffer.addByte((value >> (i - 7)) & 0xff);
currentBitOffset = 7;
currentByte = 0;
i -= 8;
} else {
currentByte |= ((value >> i) & 1) << currentBitOffset;
currentBitOffset--;
if (currentBitOffset == -1) {
buffer.addByte(currentByte);
currentBitOffset = 7;
currentByte = 0;
}
i--;
}
}
}
for (var i = 0; i < bytes.length; i++) {
var byte = bytes[i];
var value = _codewords[byte];
writeValue(value.encodedBytes, value.numBits);
}
if (currentBitOffset < 7) {
writeValue(0xff, 1 + currentBitOffset);
}
return buffer.takeBytes();
}
}
///生成h2 huffman解码tree
HuffmanTreeNode generateHuffmanTree(List<EncodedHuffmanValue> valueEncodings) {
var root = HuffmanTreeNode();
for (var byteOffset = 0; byteOffset < valueEncodings.length; byteOffset++) {
var entry = valueEncodings[byteOffset];
var current = root;
for (var bitNr = 0; bitNr < entry.numBits; bitNr++) {
var right = ((entry.encodedBytes >> (entry.numBits - bitNr - 1)) & 1) == 1;
if (right) {
current.right ??= HuffmanTreeNode();
current = current.right!;
} else {
current.left ??= HuffmanTreeNode();
current = current.left!;
}
}
current.value = byteOffset;
}
return root;
}
class HuffmanTreeNode {
int? value;
HuffmanTreeNode? left;
HuffmanTreeNode? right;
bool isLeaf() {
return left == null && right == null;
}
}
//HPACK规范 huffman编码的字节编码列表。
final List<EncodedHuffmanValue> _huffmanTable = [
EncodedHuffmanValue(0x1ff8, 13),
EncodedHuffmanValue(0x7fffd8, 23),
EncodedHuffmanValue(0xfffffe2, 28),
EncodedHuffmanValue(0xfffffe3, 28),
EncodedHuffmanValue(0xfffffe4, 28),
EncodedHuffmanValue(0xfffffe5, 28),
EncodedHuffmanValue(0xfffffe6, 28),
EncodedHuffmanValue(0xfffffe7, 28),
EncodedHuffmanValue(0xfffffe8, 28),
EncodedHuffmanValue(0xffffea, 24),
EncodedHuffmanValue(0x3ffffffc, 30),
EncodedHuffmanValue(0xfffffe9, 28),
EncodedHuffmanValue(0xfffffea, 28),
EncodedHuffmanValue(0x3ffffffd, 30),
EncodedHuffmanValue(0xfffffeb, 28),
EncodedHuffmanValue(0xfffffec, 28),
EncodedHuffmanValue(0xfffffed, 28),
EncodedHuffmanValue(0xfffffee, 28),
EncodedHuffmanValue(0xfffffef, 28),
EncodedHuffmanValue(0xffffff0, 28),
EncodedHuffmanValue(0xffffff1, 28),
EncodedHuffmanValue(0xffffff2, 28),
EncodedHuffmanValue(0x3ffffffe, 30),
EncodedHuffmanValue(0xffffff3, 28),
EncodedHuffmanValue(0xffffff4, 28),
EncodedHuffmanValue(0xffffff5, 28),
EncodedHuffmanValue(0xffffff6, 28),
EncodedHuffmanValue(0xffffff7, 28),
EncodedHuffmanValue(0xffffff8, 28),
EncodedHuffmanValue(0xffffff9, 28),
EncodedHuffmanValue(0xffffffa, 28),
EncodedHuffmanValue(0xffffffb, 28),
EncodedHuffmanValue(0x14, 6),
EncodedHuffmanValue(0x3f8, 10),
EncodedHuffmanValue(0x3f9, 10),
EncodedHuffmanValue(0xffa, 12),
EncodedHuffmanValue(0x1ff9, 13),
EncodedHuffmanValue(0x15, 6),
EncodedHuffmanValue(0xf8, 8),
EncodedHuffmanValue(0x7fa, 11),
EncodedHuffmanValue(0x3fa, 10),
EncodedHuffmanValue(0x3fb, 10),
EncodedHuffmanValue(0xf9, 8),
EncodedHuffmanValue(0x7fb, 11),
EncodedHuffmanValue(0xfa, 8),
EncodedHuffmanValue(0x16, 6),
EncodedHuffmanValue(0x17, 6),
EncodedHuffmanValue(0x18, 6),
EncodedHuffmanValue(0x0, 5),
EncodedHuffmanValue(0x1, 5),
EncodedHuffmanValue(0x2, 5),
EncodedHuffmanValue(0x19, 6),
EncodedHuffmanValue(0x1a, 6),
EncodedHuffmanValue(0x1b, 6),
EncodedHuffmanValue(0x1c, 6),
EncodedHuffmanValue(0x1d, 6),
EncodedHuffmanValue(0x1e, 6),
EncodedHuffmanValue(0x1f, 6),
EncodedHuffmanValue(0x5c, 7),
EncodedHuffmanValue(0xfb, 8),
EncodedHuffmanValue(0x7ffc, 15),
EncodedHuffmanValue(0x20, 6),
EncodedHuffmanValue(0xffb, 12),
EncodedHuffmanValue(0x3fc, 10),
EncodedHuffmanValue(0x1ffa, 13),
EncodedHuffmanValue(0x21, 6),
EncodedHuffmanValue(0x5d, 7),
EncodedHuffmanValue(0x5e, 7),
EncodedHuffmanValue(0x5f, 7),
EncodedHuffmanValue(0x60, 7),
EncodedHuffmanValue(0x61, 7),
EncodedHuffmanValue(0x62, 7),
EncodedHuffmanValue(0x63, 7),
EncodedHuffmanValue(0x64, 7),
EncodedHuffmanValue(0x65, 7),
EncodedHuffmanValue(0x66, 7),
EncodedHuffmanValue(0x67, 7),
EncodedHuffmanValue(0x68, 7),
EncodedHuffmanValue(0x69, 7),
EncodedHuffmanValue(0x6a, 7),
EncodedHuffmanValue(0x6b, 7),
EncodedHuffmanValue(0x6c, 7),
EncodedHuffmanValue(0x6d, 7),
EncodedHuffmanValue(0x6e, 7),
EncodedHuffmanValue(0x6f, 7),
EncodedHuffmanValue(0x70, 7),
EncodedHuffmanValue(0x71, 7),
EncodedHuffmanValue(0x72, 7),
EncodedHuffmanValue(0xfc, 8),
EncodedHuffmanValue(0x73, 7),
EncodedHuffmanValue(0xfd, 8),
EncodedHuffmanValue(0x1ffb, 13),
EncodedHuffmanValue(0x7fff0, 19),
EncodedHuffmanValue(0x1ffc, 13),
EncodedHuffmanValue(0x3ffc, 14),
EncodedHuffmanValue(0x22, 6),
EncodedHuffmanValue(0x7ffd, 15),
EncodedHuffmanValue(0x3, 5),
EncodedHuffmanValue(0x23, 6),
EncodedHuffmanValue(0x4, 5),
EncodedHuffmanValue(0x24, 6),
EncodedHuffmanValue(0x5, 5),
EncodedHuffmanValue(0x25, 6),
EncodedHuffmanValue(0x26, 6),
EncodedHuffmanValue(0x27, 6),
EncodedHuffmanValue(0x6, 5),
EncodedHuffmanValue(0x74, 7),
EncodedHuffmanValue(0x75, 7),
EncodedHuffmanValue(0x28, 6),
EncodedHuffmanValue(0x29, 6),
EncodedHuffmanValue(0x2a, 6),
EncodedHuffmanValue(0x7, 5),
EncodedHuffmanValue(0x2b, 6),
EncodedHuffmanValue(0x76, 7),
EncodedHuffmanValue(0x2c, 6),
EncodedHuffmanValue(0x8, 5),
EncodedHuffmanValue(0x9, 5),
EncodedHuffmanValue(0x2d, 6),
EncodedHuffmanValue(0x77, 7),
EncodedHuffmanValue(0x78, 7),
EncodedHuffmanValue(0x79, 7),
EncodedHuffmanValue(0x7a, 7),
EncodedHuffmanValue(0x7b, 7),
EncodedHuffmanValue(0x7ffe, 15),
EncodedHuffmanValue(0x7fc, 11),
EncodedHuffmanValue(0x3ffd, 14),
EncodedHuffmanValue(0x1ffd, 13),
EncodedHuffmanValue(0xffffffc, 28),
EncodedHuffmanValue(0xfffe6, 20),
EncodedHuffmanValue(0x3fffd2, 22),
EncodedHuffmanValue(0xfffe7, 20),
EncodedHuffmanValue(0xfffe8, 20),
EncodedHuffmanValue(0x3fffd3, 22),
EncodedHuffmanValue(0x3fffd4, 22),
EncodedHuffmanValue(0x3fffd5, 22),
EncodedHuffmanValue(0x7fffd9, 23),
EncodedHuffmanValue(0x3fffd6, 22),
EncodedHuffmanValue(0x7fffda, 23),
EncodedHuffmanValue(0x7fffdb, 23),
EncodedHuffmanValue(0x7fffdc, 23),
EncodedHuffmanValue(0x7fffdd, 23),
EncodedHuffmanValue(0x7fffde, 23),
EncodedHuffmanValue(0xffffeb, 24),
EncodedHuffmanValue(0x7fffdf, 23),
EncodedHuffmanValue(0xffffec, 24),
EncodedHuffmanValue(0xffffed, 24),
EncodedHuffmanValue(0x3fffd7, 22),
EncodedHuffmanValue(0x7fffe0, 23),
EncodedHuffmanValue(0xffffee, 24),
EncodedHuffmanValue(0x7fffe1, 23),
EncodedHuffmanValue(0x7fffe2, 23),
EncodedHuffmanValue(0x7fffe3, 23),
EncodedHuffmanValue(0x7fffe4, 23),
EncodedHuffmanValue(0x1fffdc, 21),
EncodedHuffmanValue(0x3fffd8, 22),
EncodedHuffmanValue(0x7fffe5, 23),
EncodedHuffmanValue(0x3fffd9, 22),
EncodedHuffmanValue(0x7fffe6, 23),
EncodedHuffmanValue(0x7fffe7, 23),
EncodedHuffmanValue(0xffffef, 24),
EncodedHuffmanValue(0x3fffda, 22),
EncodedHuffmanValue(0x1fffdd, 21),
EncodedHuffmanValue(0xfffe9, 20),
EncodedHuffmanValue(0x3fffdb, 22),
EncodedHuffmanValue(0x3fffdc, 22),
EncodedHuffmanValue(0x7fffe8, 23),
EncodedHuffmanValue(0x7fffe9, 23),
EncodedHuffmanValue(0x1fffde, 21),
EncodedHuffmanValue(0x7fffea, 23),
EncodedHuffmanValue(0x3fffdd, 22),
EncodedHuffmanValue(0x3fffde, 22),
EncodedHuffmanValue(0xfffff0, 24),
EncodedHuffmanValue(0x1fffdf, 21),
EncodedHuffmanValue(0x3fffdf, 22),
EncodedHuffmanValue(0x7fffeb, 23),
EncodedHuffmanValue(0x7fffec, 23),
EncodedHuffmanValue(0x1fffe0, 21),
EncodedHuffmanValue(0x1fffe1, 21),
EncodedHuffmanValue(0x3fffe0, 22),
EncodedHuffmanValue(0x1fffe2, 21),
EncodedHuffmanValue(0x7fffed, 23),
EncodedHuffmanValue(0x3fffe1, 22),
EncodedHuffmanValue(0x7fffee, 23),
EncodedHuffmanValue(0x7fffef, 23),
EncodedHuffmanValue(0xfffea, 20),
EncodedHuffmanValue(0x3fffe2, 22),
EncodedHuffmanValue(0x3fffe3, 22),
EncodedHuffmanValue(0x3fffe4, 22),
EncodedHuffmanValue(0x7ffff0, 23),
EncodedHuffmanValue(0x3fffe5, 22),
EncodedHuffmanValue(0x3fffe6, 22),
EncodedHuffmanValue(0x7ffff1, 23),
EncodedHuffmanValue(0x3ffffe0, 26),
EncodedHuffmanValue(0x3ffffe1, 26),
EncodedHuffmanValue(0xfffeb, 20),
EncodedHuffmanValue(0x7fff1, 19),
EncodedHuffmanValue(0x3fffe7, 22),
EncodedHuffmanValue(0x7ffff2, 23),
EncodedHuffmanValue(0x3fffe8, 22),
EncodedHuffmanValue(0x1ffffec, 25),
EncodedHuffmanValue(0x3ffffe2, 26),
EncodedHuffmanValue(0x3ffffe3, 26),
EncodedHuffmanValue(0x3ffffe4, 26),
EncodedHuffmanValue(0x7ffffde, 27),
EncodedHuffmanValue(0x7ffffdf, 27),
EncodedHuffmanValue(0x3ffffe5, 26),
EncodedHuffmanValue(0xfffff1, 24),
EncodedHuffmanValue(0x1ffffed, 25),
EncodedHuffmanValue(0x7fff2, 19),
EncodedHuffmanValue(0x1fffe3, 21),
EncodedHuffmanValue(0x3ffffe6, 26),
EncodedHuffmanValue(0x7ffffe0, 27),
EncodedHuffmanValue(0x7ffffe1, 27),
EncodedHuffmanValue(0x3ffffe7, 26),
EncodedHuffmanValue(0x7ffffe2, 27),
EncodedHuffmanValue(0xfffff2, 24),
EncodedHuffmanValue(0x1fffe4, 21),
EncodedHuffmanValue(0x1fffe5, 21),
EncodedHuffmanValue(0x3ffffe8, 26),
EncodedHuffmanValue(0x3ffffe9, 26),
EncodedHuffmanValue(0xffffffd, 28),
EncodedHuffmanValue(0x7ffffe3, 27),
EncodedHuffmanValue(0x7ffffe4, 27),
EncodedHuffmanValue(0x7ffffe5, 27),
EncodedHuffmanValue(0xfffec, 20),
EncodedHuffmanValue(0xfffff3, 24),
EncodedHuffmanValue(0xfffed, 20),
EncodedHuffmanValue(0x1fffe6, 21),
EncodedHuffmanValue(0x3fffe9, 22),
EncodedHuffmanValue(0x1fffe7, 21),
EncodedHuffmanValue(0x1fffe8, 21),
EncodedHuffmanValue(0x7ffff3, 23),
EncodedHuffmanValue(0x3fffea, 22),
EncodedHuffmanValue(0x3fffeb, 22),
EncodedHuffmanValue(0x1ffffee, 25),
EncodedHuffmanValue(0x1ffffef, 25),
EncodedHuffmanValue(0xfffff4, 24),
EncodedHuffmanValue(0xfffff5, 24),
EncodedHuffmanValue(0x3ffffea, 26),
EncodedHuffmanValue(0x7ffff4, 23),
EncodedHuffmanValue(0x3ffffeb, 26),
EncodedHuffmanValue(0x7ffffe6, 27),
EncodedHuffmanValue(0x3ffffec, 26),
EncodedHuffmanValue(0x3ffffed, 26),
EncodedHuffmanValue(0x7ffffe7, 27),
EncodedHuffmanValue(0x7ffffe8, 27),
EncodedHuffmanValue(0x7ffffe9, 27),
EncodedHuffmanValue(0x7ffffea, 27),
EncodedHuffmanValue(0x7ffffeb, 27),
EncodedHuffmanValue(0xffffffe, 28),
EncodedHuffmanValue(0x7ffffec, 27),
EncodedHuffmanValue(0x7ffffed, 27),
EncodedHuffmanValue(0x7ffffee, 27),
EncodedHuffmanValue(0x7ffffef, 27),
EncodedHuffmanValue(0x7fffff0, 27),
EncodedHuffmanValue(0x3ffffee, 26),
EncodedHuffmanValue(0x3fffffff, 30),
];
class EncodedHuffmanValue {
final int encodedBytes;
final int numBits;
EncodedHuffmanValue(this.encodedBytes, this.numBits);
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2023 the original author or authors.
*
* 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 'package:network_proxy/network/channel.dart';
import 'package:network_proxy/network/http/h2/frame.dart';
import 'package:network_proxy/network/util/byte_buf.dart';
class StreamSetting {
/// 允许发送方通知远程端点用于解码头块的头压缩表的最大大小(以八位字节为单位)。
/// 初始值为4096个八位字节。
int headTableSize = 4096;
///如果一个端点接收到的这个参数设置为0它就不能发送PUSH_PROMISE帧。
///初始值为1表示允许服务器推送。
bool enablePush = true;
///指示发送方允许的最大并发流数。这个限制是定向的它适用于发送方允许接收方创建的流的数量。最初对该值没有限制。建议此值不小于100以免不必要地限制并行性。
int? maxConcurrentStreams;
/// 指示发送方用于流级流控制的初始窗口大小以八位字节为单位。初始值为216-165535个八位字节。
int initialWindowSize = 65535;
///表示发送方愿意接收的最大帧有效负载的大小(以八位字节为单位)。
int maxFrameSize = 16384;
///建议设置通知对等方发送方准备接受的头列表的最大大小(以八位字节为单位)。
///该值基于头字段的未压缩大小包括名称和值的长度以八位字节为单位加上每个头字段32个八位字节的开销。
int? maxHeaderListSize;
}
class SettingHandler {
static void handleSettingsFrame(ChannelContext channelContext, FrameHeader frameHeader, ByteBuf payload) {
// SETTINGS frames must have a length that is a multiple of 6 bytes
if (frameHeader.length % 6 != 0) {
throw Exception("Invalid SETTINGS frame length");
}
// If the SETTINGS frame has the ACK flag set, then it is an acknowledgement
if (frameHeader.hasAckFlag) {
// Handle SETTINGS ACK
return;
}
var setting = channelContext.setting ??= StreamSetting();
// Otherwise, it is a SETTINGS frame that carries settings
while (payload.isReadable()) {
int identifier = payload.readShort();
int value = payload.readInt();
// logger.d("SettingHandler.handleSettingsFrame identifier=$identifier value=$value");
// Handle the setting based on its identifier
switch (identifier) {
case 1: // SETTINGS_HEADER_TABLE_SIZE
setting.maxFrameSize = value;
break;
case 2: // SETTINGS_ENABLE_PUSH
setting.enablePush = value == 1;
break;
case 3: // SETTINGS_MAX_CONCURRENT_STREAMS
setting.maxConcurrentStreams = value;
break;
case 4: // SETTINGS_INITIAL_WINDOW_SIZE
setting.initialWindowSize = value;
break;
case 5: // SETTINGS_MAX_FRAME_SIZE
setting.maxFrameSize = value;
break;
case 6: // SETTINGS_MAX_HEADER_LIST_SIZE
setting.maxHeaderListSize = value;
default:
break;
}
}
}
}

View File

@@ -15,6 +15,7 @@
*/
import 'dart:convert';
import 'dart:math';
import 'package:network_proxy/network/host_port.dart';
import 'package:network_proxy/network/http/websocket.dart';
@@ -38,10 +39,11 @@ abstract class HttpMessage {
"application/json": ContentType.json
};
final String protocolVersion;
String protocolVersion;
final HttpHeaders headers = HttpHeaders();
int contentLength = -1;
int get contentLength => headers.contentLength;
//报文大小
int? packageSize;
@@ -49,6 +51,8 @@ abstract class HttpMessage {
List<int>? body;
String? remoteAddress;
String requestId = (DateTime.now().millisecondsSinceEpoch + Random().nextInt(99999)).toRadixString(16);
int? streamId; // http2 streamId
HttpMessage(this.protocolVersion);
//json序列化
@@ -145,7 +149,6 @@ class HttpRequest extends HttpMessage {
HttpRequest copy({String? uri}) {
var request = HttpRequest(method, uri ?? this.uri, protocolVersion: protocolVersion);
request.headers.addAll(headers);
request.contentLength = contentLength;
request.body = body;
return request;
}

View File

@@ -106,7 +106,7 @@ class HttpHeaders {
return value.toLowerCase() == "true";
}
int get contentLength => getInt(CONTENT_LENGTH) ?? -1;
int get contentLength => getInt(CONTENT_LENGTH) ?? 0;
set contentLength(int contentLength) => set(CONTENT_LENGTH, contentLength.toString());

View File

@@ -1,7 +1,9 @@
import 'dart:typed_data';
import 'package:network_proxy/network/http/codec.dart';
import 'package:network_proxy/network/http/constants.dart';
import 'package:network_proxy/network/http/http_headers.dart';
import 'package:network_proxy/network/util/byte_buf.dart';
/// http解析器
class HttpParse {
@@ -22,7 +24,7 @@ class HttpParse {
}
if (initialLine.length != 3) {
throw ParserException("parseLine error", String.fromCharCodes(data.buffer));
throw ParserException("parseLine error", String.fromCharCodes(data.bytes));
}
return initialLine;

View File

@@ -28,15 +28,17 @@ import 'http/codec.dart';
class HttpClients {
/// 建立连接
static Future<Channel> startConnect(HostAndPort hostAndPort, ChannelHandler handler) async {
static Future<Channel> startConnect(
HostAndPort hostAndPort, ChannelHandler handler, ChannelContext channelContext) async {
var client = Client()
..initChannel((channel) => channel.pipeline.handle(HttpResponseCodec(), HttpRequestCodec(), handler));
return client.connect(hostAndPort);
return client.connect(hostAndPort, channelContext);
}
///代理建立连接
static Future<Channel> proxyConnect(HostAndPort hostAndPort, ChannelHandler handler, {ProxyInfo? proxyInfo}) async {
static Future<Channel> proxyConnect(HostAndPort hostAndPort, ChannelHandler handler, ChannelContext channelContext,
{ProxyInfo? proxyInfo}) async {
var client = Client()
..initChannel((channel) => channel.pipeline.handle(HttpResponseCodec(), HttpRequestCodec(), handler));
@@ -46,7 +48,7 @@ class HttpClients {
}
HostAndPort connectHost = proxyInfo == null ? hostAndPort : HostAndPort.host(proxyInfo.host, proxyInfo.port!);
var channel = await client.connect(connectHost);
var channel = await client.connect(connectHost, channelContext);
if (proxyInfo == null || !hostAndPort.isSsl()) {
return channel;
@@ -74,14 +76,14 @@ class HttpClients {
}
/// 建立连接
static Future<Channel> connect(Uri uri, ChannelHandler handler) async {
static Future<Channel> connect(Uri uri, ChannelHandler handler, ChannelContext channelContext) async {
Client client = Client()
..initChannel((channel) => channel.pipeline.handle(HttpResponseCodec(), HttpRequestCodec(), handler));
if (uri.scheme == "https" || uri.scheme == "wss") {
return client.secureConnect(HostAndPort.of(uri.toString()));
return client.secureConnect(HostAndPort.of(uri.toString()), channelContext);
}
return client.connect(HostAndPort.of(uri.toString()));
return client.connect(HostAndPort.of(uri.toString()), channelContext);
}
/// 发送get请求
@@ -98,7 +100,8 @@ class HttpClients {
var client = Client()
..initChannel((channel) => channel.pipeline.handle(HttpResponseCodec(), HttpRequestCodec(), httpResponseHandler));
Channel channel = await client.connect(hostAndPort);
ChannelContext channelContext = ChannelContext();
Channel channel = await client.connect(hostAndPort, channelContext);
await channel.write(request);
return httpResponseHandler.getResponse(duration).whenComplete(() => channel.close());
@@ -112,13 +115,16 @@ class HttpClients {
request.headers.host = '${Uri.parse(request.uri).host}:${Uri.parse(request.uri).port}';
} catch (_) {}
}
request.protocolVersion = 'HTTP/1.1';
ChannelContext channelContext = ChannelContext();
var httpResponseHandler = HttpResponseHandler();
HostAndPort hostPort = HostAndPort.of(request.uri);
Channel channel = await proxyConnect(proxyInfo: proxyInfo, hostPort, httpResponseHandler);
Channel channel = await proxyConnect(proxyInfo: proxyInfo, hostPort, httpResponseHandler, channelContext);
if (hostPort.isSsl()) {
channel.secureSocket = await SecureSocket.secure(channel.socket, onBadCertificate: (certificate) => true);
var secureSocket = await SecureSocket.secure(channel.socket, onBadCertificate: (certificate) => true);
channel.secureSocket(secureSocket, channelContext);
}
await channel.write(request);
@@ -130,7 +136,7 @@ class HttpResponseHandler extends ChannelHandler<HttpResponse> {
Completer<HttpResponse> _completer = Completer<HttpResponse>();
@override
void channelRead(Channel channel, HttpResponse msg) {
void channelRead(ChannelContext channelContext, Channel channel, HttpResponse msg) {
// log.i("[${channel.id}] Response $msg");
_completer.complete(msg);
}
@@ -144,7 +150,7 @@ class HttpResponseHandler extends ChannelHandler<HttpResponse> {
}
@override
void channelInactive(Channel channel) {
void channelInactive(ChannelContext channelContext, Channel channel) {
// log.i("[${channel.id}] channelInactive");
}
}

View File

@@ -20,112 +20,35 @@ import 'dart:typed_data';
import 'package:network_proxy/network/bin/configuration.dart';
import 'package:network_proxy/network/channel.dart';
import 'package:network_proxy/network/components/host_filter.dart';
import 'package:network_proxy/network/handler.dart';
import 'package:network_proxy/network/http_client.dart';
import 'package:network_proxy/network/util/attribute_keys.dart';
import 'package:network_proxy/network/util/crts.dart';
import 'package:network_proxy/network/components/host_filter.dart';
import 'package:network_proxy/network/util/tls.dart';
import 'package:network_proxy/utils/platform.dart';
import 'host_port.dart';
class Network {
abstract class Network {
late Function _channelInitializer;
Configuration? configuration;
StreamSubscription? subscription;
Network initChannel(void Function(Channel channel) initializer) {
_channelInitializer = initializer;
return this;
}
Channel listen(Socket socket) {
var channel = Channel(socket);
Channel listen(Channel channel, ChannelContext channelContext) {
_channelInitializer.call(channel);
channel.pipeline.channelActive(channel);
subscription = socket.listen((data) => _onEvent(data, channel),
onError: (error, StackTrace trace) => channel.pipeline.exceptionCaught(channel, error, trace: trace),
onDone: () => channel.pipeline.channelInactive(channel));
channel.pipeline.channelActive(channelContext, channel);
channel.socket.listen((data) => onEvent(data, channelContext, channel),
onError: (error, StackTrace trace) =>
channel.pipeline.exceptionCaught(channelContext, channel, error, trace: trace),
onDone: () => channel.pipeline.channelInactive(channelContext, channel));
return channel;
}
_onEvent(Uint8List data, Channel channel) async {
//手机扫码转发远程地址
if (configuration?.remoteHost != null) {
channel.putAttribute(AttributeKeys.remote, HostAndPort.of(configuration!.remoteHost!));
}
//外部代理信息
if (configuration?.externalProxy?.enabled == true) {
ProxyInfo externalProxy = configuration!.externalProxy!;
if (externalProxy.capturePacket == true) {
channel.putAttribute(AttributeKeys.proxyInfo, externalProxy);
} else {
//不抓包直接转发
channel.putAttribute(AttributeKeys.remote, HostAndPort.host(externalProxy.host, externalProxy.port!));
}
}
HostAndPort? hostAndPort = channel.getAttribute(AttributeKeys.host);
//黑名单 或 没开启https 直接转发
if ((Platforms.isMobile() && HostFilter.filter(hostAndPort?.host)) ||
(hostAndPort?.isSsl() == true && configuration?.enableSsl == false)) {
relay(channel, channel.getAttribute(channel.id));
channel.pipeline.channelRead(channel, data);
return;
}
//ssl握手
if (hostAndPort?.isSsl() == true || TLS.isTLSClientHello(data)) {
if (hostAndPort?.scheme == HostAndPort.httpScheme) {
hostAndPort?.scheme = HostAndPort.httpsScheme;
}
ssl(channel, hostAndPort, data);
return;
}
channel.pipeline.channelRead(channel, data);
}
/// ssl握手
void ssl(Channel channel, HostAndPort? hostAndPort, Uint8List data) async {
try {
if (hostAndPort == null && TLS.getDomain(data) != null) {
hostAndPort = HostAndPort.host(TLS.getDomain(data)!, 443);
}
channel.putAttribute(AttributeKeys.domain, hostAndPort?.host);
Channel? remoteChannel = channel.getAttribute(channel.id);
if (HostFilter.filter(hostAndPort?.host)) {
remoteChannel = remoteChannel ?? await HttpClients.startConnect(hostAndPort!, RelayHandler(channel));
relay(channel, remoteChannel);
channel.pipeline.channelRead(channel, data);
return;
}
if (remoteChannel != null && !remoteChannel.isSsl) {
// var supportProtocols = TLS.supportProtocols(data);
remoteChannel.secureSocket = await SecureSocket.secure(remoteChannel.socket,
host: hostAndPort?.host, onBadCertificate: (certificate) => true);
}
//ssl自签证书
var certificate = await CertificateManager.getCertificateContext(hostAndPort!.host);
// var selectedProtocol = remoteChannel?.selectedProtocol;
// if (selectedProtocol != null) certificate.setAlpnProtocols([selectedProtocol], true);
//服务端等待客户端ssl握手
channel.secureSocket = await SecureSocket.secureServer(channel.socket, certificate, bufferedData: data);
} catch (error, trace) {
if (error is HandshakeException) {
channel.putAttribute(AttributeKeys.host, hostAndPort);
}
channel.pipeline.exceptionCaught(channel, error, trace: trace);
}
}
Future<void> onEvent(Uint8List data, ChannelContext channelContext, Channel channel);
/// 转发请求
void relay(Channel clientChannel, Channel remoteChannel) {
@@ -136,17 +59,22 @@ class Network {
}
class Server extends Network {
Configuration configuration;
late ServerSocket serverSocket;
bool isRunning = false;
EventListener? listener;
Server(Configuration configuration) {
super.configuration = configuration;
}
Server(this.configuration, {this.listener});
Future<ServerSocket> bind(int port) async {
serverSocket = await ServerSocket.bind(InternetAddress.anyIPv4, port);
serverSocket.listen((socket) {
listen(socket);
var channel = Channel(socket);
ChannelContext channelContext = ChannelContext();
channelContext.clientChannel = channel;
channelContext.listener = listener;
listen(channel, channelContext);
});
isRunning = true;
return serverSocket;
@@ -158,10 +86,93 @@ class Server extends Network {
await serverSocket.close();
return serverSocket;
}
@override
Future<void> onEvent(Uint8List data, ChannelContext channelContext, Channel channel) async {
//手机扫码转发远程地址
if (configuration.remoteHost != null) {
channelContext.putAttribute(AttributeKeys.remote, HostAndPort.of(configuration.remoteHost!));
}
//外部代理信息
if (configuration.externalProxy?.enabled == true) {
ProxyInfo externalProxy = configuration.externalProxy!;
if (externalProxy.capturePacket == true) {
channelContext.putAttribute(AttributeKeys.proxyInfo, externalProxy);
} else {
//不抓包直接转发
channelContext.putAttribute(AttributeKeys.remote, HostAndPort.host(externalProxy.host, externalProxy.port!));
}
}
HostAndPort? hostAndPort = channelContext.host;
//黑名单 或 没开启https 直接转发
if ((Platforms.isMobile() && HostFilter.filter(hostAndPort?.host)) ||
(hostAndPort?.isSsl() == true && configuration.enableSsl == false)) {
var remoteChannel = channelContext.serverChannel ??
await channelContext.connectServerChannel(hostAndPort!, RelayHandler(channel));
relay(channel, remoteChannel);
channel.pipeline.channelRead(channelContext, channel, data);
return;
}
//ssl握手
if (hostAndPort?.isSsl() == true || TLS.isTLSClientHello(data)) {
if (hostAndPort?.scheme == HostAndPort.httpScheme) {
hostAndPort?.scheme = HostAndPort.httpsScheme;
}
ssl(channelContext, channel, data);
return;
}
channel.pipeline.channelRead(channelContext, channel, data);
}
/// ssl握手
void ssl(ChannelContext channelContext, Channel channel, Uint8List data) async {
var hostAndPort = channelContext.host;
try {
if (hostAndPort == null && TLS.getDomain(data) != null) {
hostAndPort = HostAndPort.host(TLS.getDomain(data)!, 443);
}
channelContext.putAttribute(AttributeKeys.domain, hostAndPort?.host);
Channel? remoteChannel = channelContext.serverChannel;
if (HostFilter.filter(hostAndPort?.host)) {
remoteChannel = remoteChannel ?? await channelContext.connectServerChannel(hostAndPort!, RelayHandler(channel));
relay(channel, remoteChannel);
channel.pipeline.channelRead(channelContext, channel, data);
return;
}
if (remoteChannel != null && !remoteChannel.isSsl) {
var supportProtocols = configuration.enabledHttp2 ? TLS.supportProtocols(data) : null;
var secureSocket = await SecureSocket.secure(remoteChannel.socket,
supportedProtocols: supportProtocols, host: hostAndPort?.host, onBadCertificate: (certificate) => true);
remoteChannel.secureSocket(secureSocket, channelContext);
}
//ssl自签证书
var certificate = await CertificateManager.getCertificateContext(hostAndPort!.host);
var selectedProtocol = remoteChannel?.selectedProtocol;
if (selectedProtocol != null) certificate.setAlpnProtocols([selectedProtocol], true);
//服务端等待客户端ssl握手
var secureSocket = await SecureSocket.secureServer(channel.socket, certificate, bufferedData: data);
channel.secureSocket(secureSocket, channelContext);
} catch (error, trace) {
if (error is HandshakeException) {
channelContext.host = hostAndPort;
}
channel.pipeline.exceptionCaught(channelContext, channel, error, trace: trace);
}
}
}
class Client extends Network {
Future<Channel> connect(HostAndPort hostAndPort) async {
Future<Channel> connect(HostAndPort hostAndPort, ChannelContext channelContext) async {
String host = hostAndPort.host;
//说明支持ipv6
if (host.startsWith("[") && host.endsWith(']')) {
@@ -172,13 +183,24 @@ class Client extends Network {
if (socket.address.type != InternetAddressType.unix) {
socket.setOption(SocketOption.tcpNoDelay, true);
}
return listen(socket);
var channel = Channel(socket);
channelContext.serverChannel = channel;
return listen(channel, channelContext);
});
}
/// ssl连接
Future<Channel> secureConnect(HostAndPort hostAndPort) async {
Future<Channel> secureConnect(HostAndPort hostAndPort, ChannelContext channelContext) async {
return SecureSocket.connect(hostAndPort.host, hostAndPort.port,
timeout: const Duration(seconds: 3), onBadCertificate: (certificate) => true).then((socket) => listen(socket));
timeout: const Duration(seconds: 3), onBadCertificate: (certificate) => true).then((socket) {
var channel = Channel(socket);
channelContext.serverChannel = channel;
return listen(channel, channelContext);
});
}
@override
Future<void> onEvent(Uint8List data, ChannelContext channelContext, Channel channel) async {
channel.pipeline.channelRead(channelContext, channel, data);
}
}

View File

@@ -9,15 +9,13 @@ import 'package:network_proxy/network/host_port.dart';
import 'package:network_proxy/network/http/codec.dart';
import 'package:network_proxy/network/http/http.dart';
import 'package:network_proxy/network/http/http_headers.dart';
import 'package:network_proxy/network/util/attribute_keys.dart';
import 'package:network_proxy/network/util/file_read.dart';
import 'components/host_filter.dart';
class ProxyHelper {
//请求本服务
static localRequest(HttpRequest msg, Channel channel) async {
static localRequest(HttpRequest msg, Channel channel) async {
//获取配置
if (msg.path() == '/config') {
final requestRewrites = await RequestRewrites.instance;
@@ -46,7 +44,7 @@ class ProxyHelper {
}
/// 下载证书
static void crtDownload(Channel channel, HttpRequest request) async {
static void crtDownload(Channel channel, HttpRequest request) async {
const String fileMimeType = 'application/x-x509-ca-cert';
var response = HttpResponse(HttpStatus.ok);
response.headers.set(HttpHeaders.CONTENT_TYPE, fileMimeType);
@@ -65,8 +63,9 @@ class ProxyHelper {
}
///异常处理
static exceptionHandler(Channel channel, EventListener? listener, HttpRequest? request, error) {
HostAndPort? hostAndPort = channel.getAttribute(AttributeKeys.host);
static exceptionHandler(
ChannelContext channelContext, Channel channel, EventListener? listener, HttpRequest? request, error) {
HostAndPort? hostAndPort = channelContext.host;
hostAndPort ??= HostAndPort.host(scheme: HostAndPort.httpScheme, channel.remoteAddress.host, channel.remotePort);
String message = error.toString();
HttpStatus status = HttpStatus(-1, message);
@@ -89,10 +88,11 @@ class ProxyHelper {
..headers.contentType = 'text/plain'
..headers.contentLength = message.codeUnits.length
..body = message.codeUnits;
request.response?.request = request;
channel.putAttribute(AttributeKeys.host, hostAndPort);
channelContext.host = hostAndPort;
listener?.onRequest(channel, request);
listener?.onResponse(channel, request.response!);
listener?.onResponse(channelContext, request.response!);
}
}
}

View File

@@ -0,0 +1,82 @@
import 'dart:typed_data';
///类似于netty ByteBuf
class ByteBuf {
BytesBuilder _buffer = BytesBuilder();
int readerIndex = 0;
Uint8List get bytes => _buffer.toBytes();
int get length => _buffer.length;
ByteBuf([List<int>? bytes]) {
if (bytes != null) _buffer.add(bytes);
}
///添加
void add(List<int> bytes) {
_buffer.add(bytes);
}
///清空
clear() {
_buffer.clear();
readerIndex = 0;
}
///释放
clearRead() {
var takeBytes = _buffer.takeBytes();
_buffer.add(Uint8List.sublistView(takeBytes, readerIndex, takeBytes.length));
readerIndex = 0;
}
bool isReadable() => readerIndex < _buffer.length;
///可读字节数
int readableBytes() {
return _buffer.length - readerIndex;
}
///读取所有可用字节
Uint8List readAvailableBytes() {
return readBytes(readableBytes());
}
///读取字节
Uint8List readBytes(int length) {
Uint8List list = bytes.sublist(readerIndex, readerIndex + length);
readerIndex += length;
return list;
}
///跳过
skipBytes(int length) {
readerIndex += length;
}
///读取字节
int read() {
return bytes[readerIndex++];
}
///读取字节
int readByte() {
return bytes[readerIndex++];
}
int readShort() {
int value = bytes[readerIndex++] << 8 | bytes[readerIndex++];
return value;
}
int readInt() {
int value =
bytes[readerIndex++] << 24 | bytes[readerIndex++] << 16 | bytes[readerIndex++] << 8 | bytes[readerIndex++];
return value;
}
int get(int index) {
return bytes[index];
}
}

View File

@@ -184,6 +184,8 @@ class NetworkTabState extends State<NetworkTabController> with SingleTickerProvi
const SizedBox(height: 20),
rowWidget("Request Method", request.method.name),
const SizedBox(height: 20),
rowWidget("Protocol", request.protocolVersion),
const SizedBox(height: 20),
rowWidget("Status Code", response?.status.toString()),
const SizedBox(height: 20),
rowWidget("Remote Address", response?.remoteAddress),

View File

@@ -8,7 +8,7 @@ import 'package:network_proxy/network/http/websocket.dart';
import 'package:network_proxy/ui/component/state_component.dart';
import 'package:network_proxy/ui/component/toolbox.dart';
import 'package:network_proxy/ui/content/panel.dart';
import 'package:network_proxy/ui/desktop/left/domain.dart';
import 'package:network_proxy/ui/desktop/left/list.dart';
import 'package:network_proxy/ui/desktop/left/favorite.dart';
import 'package:network_proxy/ui/desktop/left/history.dart';
import 'package:network_proxy/ui/desktop/toolbar/toolbar.dart';
@@ -45,8 +45,8 @@ class _DesktopHomePagePageState extends State<DesktopHomePage> implements EventL
}
@override
void onResponse(Channel channel, HttpResponse response) {
domainStateKey.currentState!.addResponse(channel, response);
void onResponse(ChannelContext channelContext, HttpResponse response) {
domainStateKey.currentState!.addResponse(channelContext, response);
}
@override
@@ -71,7 +71,7 @@ class _DesktopHomePagePageState extends State<DesktopHomePage> implements EventL
@override
Widget build(BuildContext context) {
final domainWidget = DomainWidget(key: domainStateKey, proxyServer: proxyServer, panel: panel);
final domainWidget = DomainList(key: domainStateKey, proxyServer: proxyServer, panel: panel);
return Scaffold(
appBar: Tab(child: Toolbar(proxyServer, domainStateKey, sideNotifier: _selectIndex)),
body: Row(

View File

@@ -18,7 +18,7 @@ import 'package:network_proxy/ui/component/widgets.dart';
import 'package:network_proxy/utils/har.dart';
import '../../content/panel.dart';
import 'domain.dart';
import 'list.dart';
///历史记录
class HistoryPageWidget extends StatelessWidget {
@@ -59,7 +59,7 @@ class HistoryPageWidget extends StatelessWidget {
)),
body: futureWidget(HistoryStorage.instance.then((value) => value.getRequests(item)), (data) {
print("START ${DateTime.now()}");
return DomainWidget(panel: panel, proxyServer: proxyServer, list: data, shrinkWrap: false);
return DomainList(panel: panel, proxyServer: proxyServer, list: data, shrinkWrap: false);
}, loading: true));
}
}
@@ -287,7 +287,7 @@ class WriteTask extends EventListener {
void onRequest(Channel channel, HttpRequest request) {}
@override
void onResponse(Channel channel, HttpResponse response) {
void onResponse(ChannelContext channelContext, HttpResponse response) {
if (response.request == null) {
return;
}

View File

@@ -5,26 +5,24 @@ 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/host_port.dart';
import 'package:network_proxy/network/http/http.dart';
import 'package:network_proxy/network/util/attribute_keys.dart';
import 'package:network_proxy/network/components/host_filter.dart';
import 'package:network_proxy/network/http/http.dart';
import 'package:network_proxy/ui/component/transition.dart';
import 'package:network_proxy/ui/component/utils.dart';
import 'package:network_proxy/ui/component/widgets.dart';
import 'package:network_proxy/ui/content/panel.dart';
import 'package:network_proxy/ui/desktop/left/model/search_model.dart';
import 'package:network_proxy/ui/desktop/left/path.dart';
import 'package:network_proxy/ui/desktop/left/request.dart';
import 'package:network_proxy/ui/desktop/left/search.dart';
///
class DomainWidget extends StatefulWidget {
class DomainList extends StatefulWidget {
final NetworkTabController panel;
final ProxyServer proxyServer;
final List<HttpRequest>? list;
final bool shrinkWrap;
const DomainWidget({super.key, required this.panel, required this.proxyServer, this.list, this.shrinkWrap = true});
const DomainList({super.key, required this.panel, required this.proxyServer, this.list, this.shrinkWrap = true});
@override
State<StatefulWidget> createState() {
@@ -32,10 +30,10 @@ class DomainWidget extends StatefulWidget {
}
}
class DomainWidgetState extends State<DomainWidget> with AutomaticKeepAliveClientMixin {
class DomainWidgetState extends State<DomainList> with AutomaticKeepAliveClientMixin {
List<HttpRequest> container = [];
LinkedHashMap<HostAndPort, HeaderBody> containerMap = LinkedHashMap<HostAndPort, HeaderBody>();
LinkedHashMap<HostAndPort, HeaderBody> searchView = LinkedHashMap<HostAndPort, HeaderBody>();
LinkedHashMap<String, DomainRequests> containerMap = LinkedHashMap<String, DomainRequests>();
LinkedHashMap<String, DomainRequests> searchView = LinkedHashMap<String, DomainRequests>();
//
SearchModel? searchModel;
@@ -56,15 +54,15 @@ class DomainWidgetState extends State<DomainWidget> with AutomaticKeepAliveClien
void initState() {
super.initState();
widget.list?.forEach((request) {
var host = HostAndPort.of(request.requestUrl);
HeaderBody? headerBody = containerMap[host];
if (headerBody == null) {
headerBody = HeaderBody(host, proxyServer: widget.panel.proxyServer, onRemove: () => remove(host));
containerMap[host] = headerBody;
var host = request.remoteDomain()!;
DomainRequests? domainRequests = containerMap[host];
if (domainRequests == null) {
domainRequests = DomainRequests(host, proxyServer: widget.panel.proxyServer, onRemove: () => remove(host));
containerMap[host] = domainRequests;
}
var listURI =
PathRow(request, widget.panel, proxyServer: widget.panel.proxyServer, remove: (it) => headerBody!.remove(it));
headerBody.addBody(null, listURI);
var listURI = RequestWidget(request, widget.panel,
proxyServer: widget.panel.proxyServer, remove: (it) => domainRequests!.remove(it));
domainRequests.addBody(null, listURI);
});
}
@@ -86,8 +84,7 @@ class DomainWidgetState extends State<DomainWidget> with AutomaticKeepAliveClien
Widget body = widget.shrinkWrap
? SingleChildScrollView(child: Column(children: list.toList()))
: ListView.builder(
itemCount: list.length, cacheExtent: 1000, itemBuilder: (_, index) => list.elementAt(index));
: ListView.builder(itemCount: list.length, cacheExtent: 1000, itemBuilder: (_, index) => list.elementAt(index));
return Scaffold(
body: body,
@@ -99,13 +96,13 @@ class DomainWidgetState extends State<DomainWidget> with AutomaticKeepAliveClien
}
///
LinkedHashMap<HostAndPort, HeaderBody> searchFilter(SearchModel searchModel) {
LinkedHashMap<HostAndPort, HeaderBody> result = LinkedHashMap<HostAndPort, HeaderBody>();
LinkedHashMap<String, DomainRequests> searchFilter(SearchModel searchModel) {
LinkedHashMap<String, DomainRequests> result = LinkedHashMap<String, DomainRequests>();
containerMap.forEach((key, headerBody) {
var body = headerBody.search(searchModel);
containerMap.forEach((key, domainRequests) {
var body = domainRequests.search(searchModel);
if (body.isNotEmpty) {
result[key] = headerBody.copy(body: body, selected: searchView[key]?.currentSelected);
result[key] = domainRequests.copy(body: body, selected: searchView[key]?.currentSelected);
}
});
@@ -115,46 +112,46 @@ class DomainWidgetState extends State<DomainWidget> with AutomaticKeepAliveClien
///
add(Channel channel, HttpRequest request) {
container.add(request);
HostAndPort? hostAndPort = request.hostAndPort;
if (hostAndPort == null) {
String? host = request.remoteDomain();
if (host == null) {
return;
}
//
HeaderBody? headerBody = containerMap[hostAndPort];
if (headerBody != null) {
var listURI =
PathRow(request, widget.panel, proxyServer: widget.proxyServer, remove: (it) => headerBody!.remove(it));
headerBody.addBody(channel.id, listURI);
DomainRequests? domainRequests = containerMap[host];
if (domainRequests != null) {
var requestWidget = RequestWidget(request, widget.panel,
proxyServer: widget.proxyServer, remove: (it) => domainRequests!.remove(it));
domainRequests.addBody(request.requestId, requestWidget);
//
if (searchModel?.isNotEmpty == true && searchModel?.filter(request, null) == true) {
searchView[hostAndPort]?.addBody(channel.id, listURI);
searchView[host]?.addBody(request.requestId, requestWidget);
}
return;
}
headerBody = HeaderBody(hostAndPort, proxyServer: widget.proxyServer, onRemove: () => remove(hostAndPort));
var listURI =
PathRow(request, widget.panel, proxyServer: widget.proxyServer, remove: (it) => headerBody!.remove(it));
headerBody.addBody(channel.id, listURI);
domainRequests = DomainRequests(host, proxyServer: widget.proxyServer, onRemove: () => remove(host));
var requestWidget = RequestWidget(request, widget.panel,
proxyServer: widget.proxyServer, remove: (it) => domainRequests!.remove(it));
domainRequests.addBody(request.requestId, requestWidget);
setState(() {
containerMap[hostAndPort] = headerBody!;
containerMap[host] = domainRequests!;
});
}
remove(HostAndPort hostAndPort) {
remove(String host) {
setState(() {
containerMap.remove(hostAndPort);
container.removeWhere((element) => element.hostAndPort == hostAndPort);
containerMap.remove(host);
container.removeWhere((element) => element.remoteDomain() == host);
});
}
///
addResponse(Channel channel, HttpResponse response) {
HostAndPort hostAndPort = channel.getAttribute(AttributeKeys.host);
HeaderBody? headerBody = containerMap[hostAndPort];
var pathRow = headerBody?.getBody(channel.id);
addResponse(ChannelContext channelContext, HttpResponse response) {
String domain = channelContext.host!.domain;
DomainRequests? domainRequests = containerMap[domain];
var pathRow = domainRequests?.getRequest(response);
pathRow?.add(response);
if (pathRow == null) {
return;
@@ -162,11 +159,11 @@ class DomainWidgetState extends State<DomainWidget> with AutomaticKeepAliveClien
//
if (searchModel?.isNotEmpty == true && searchModel?.filter(pathRow.request, response) == true) {
var header = searchView[hostAndPort];
if (header?.getBody(channel.id) == null) {
header?.addBody(channel.id, pathRow);
var requests = searchView[domain];
if (requests?.getRequest(response) == null) {
requests?.addBody(response.requestId, pathRow);
}
headerBody?.getBody(channel.id)?.add(response);
requests?.getRequest(response)?.add(response);
}
}
@@ -181,17 +178,15 @@ class DomainWidgetState extends State<DomainWidget> with AutomaticKeepAliveClien
}
///
class HeaderBody extends StatefulWidget {
final stateKey = GlobalKey<_HeaderBodyState>();
class DomainRequests extends StatefulWidget {
//ID和请求的映射
final Map<String, PathRow> channelIdPathMap = HashMap<String, PathRow>();
final Map<String, RequestWidget> requestMap = HashMap<String, RequestWidget>();
final HostAndPort header;
final String domain;
final ProxyServer proxyServer;
//
final Queue<PathRow> body = Queue();
final Queue<RequestWidget> body = Queue();
//
final bool selected;
@@ -199,39 +194,39 @@ class HeaderBody extends StatefulWidget {
//
final Function()? onRemove;
HeaderBody(this.header, {this.selected = false, this.onRemove, required this.proxyServer})
: super(key: GlobalKey<_HeaderBodyState>());
DomainRequests(this.domain, {this.selected = false, this.onRemove, required this.proxyServer})
: super(key: GlobalKey<_DomainRequestsState>());
///
void addBody(String? key, PathRow widget) {
void addBody(String? requestId, RequestWidget widget) {
body.addFirst(widget);
if (key == null) {
if (requestId == null) {
return;
}
channelIdPathMap[key] = widget;
requestMap[requestId] = widget;
changeState();
}
PathRow? getBody(String key) {
return channelIdPathMap[key];
RequestWidget? getRequest(HttpResponse response) {
return requestMap[response.request?.requestId ?? response.requestId];
}
remove(PathRow pathRow) {
if (body.remove(pathRow)) {
remove(RequestWidget requestWidget) {
if (body.remove(requestWidget)) {
changeState();
}
}
///
Iterable<PathRow> search(SearchModel searchModel) {
Iterable<RequestWidget> search(SearchModel searchModel) {
return body
.where((element) => searchModel.filter(element.request, element.response.get() ?? element.request.response));
}
///
HeaderBody copy({Iterable<PathRow>? body, bool? selected}) {
var state = key as GlobalKey<_HeaderBodyState>;
var headerBody = HeaderBody(header,
DomainRequests copy({Iterable<RequestWidget>? body, bool? selected}) {
var state = key as GlobalKey<_DomainRequestsState>;
var headerBody = DomainRequests(domain,
selected: selected ?? state.currentState?.selected == true, onRemove: onRemove, proxyServer: proxyServer);
if (body != null) {
headerBody.body.addAll(body);
@@ -240,22 +235,22 @@ class HeaderBody extends StatefulWidget {
}
bool get currentSelected {
var state = key as GlobalKey<_HeaderBodyState>;
var state = key as GlobalKey<_DomainRequestsState>;
return state.currentState?.selected == true;
}
changeState() {
var state = key as GlobalKey<_HeaderBodyState>;
var state = key as GlobalKey<_DomainRequestsState>;
state.currentState?.changeState();
}
@override
State<StatefulWidget> createState() {
return _HeaderBodyState();
return _DomainRequestsState();
}
}
class _HeaderBodyState extends State<HeaderBody> {
class _DomainRequestsState extends State<DomainRequests> {
final GlobalKey<ColorTransitionState> transitionState = GlobalKey<ColorTransitionState>();
late Configuration configuration;
late bool selected;
@@ -275,7 +270,7 @@ class _HeaderBodyState extends State<HeaderBody> {
@override
Widget build(BuildContext context) {
return Column(children: [
_hostWidget(widget.header.domain),
_hostWidget(widget.domain),
Offstage(offstage: !selected, child: Column(children: widget.body.toList()))
]);
}
@@ -318,7 +313,7 @@ class _HeaderBodyState extends State<HeaderBody> {
height: 35,
child: const Text("添加黑名单", style: TextStyle(fontSize: 13)),
onTap: () {
HostFilter.blacklist.add(widget.header.host);
HostFilter.blacklist.add(Uri.parse(widget.domain).host);
configuration.flushConfig();
FlutterToastr.show('添加成功', context);
}),
@@ -326,7 +321,7 @@ class _HeaderBodyState extends State<HeaderBody> {
height: 35,
child: const Text("添加白名单", style: TextStyle(fontSize: 13)),
onTap: () {
HostFilter.whitelist.add(widget.header.host);
HostFilter.whitelist.add(Uri.parse(widget.domain).host);
configuration.flushConfig();
FlutterToastr.show('添加成功', context);
}),
@@ -334,7 +329,7 @@ class _HeaderBodyState extends State<HeaderBody> {
height: 35,
child: const Text("删除白名单", style: TextStyle(fontSize: 13)),
onTap: () {
HostFilter.whitelist.remove(widget.header.host);
HostFilter.whitelist.remove(Uri.parse(widget.domain).host);
configuration.flushConfig();
FlutterToastr.show('删除成功', context);
}),
@@ -346,7 +341,7 @@ class _HeaderBodyState extends State<HeaderBody> {
}
_delete() {
widget.channelIdPathMap.clear();
widget.requestMap.clear();
widget.body.clear();
widget.onRemove?.call();
FlutterToastr.show('删除成功', context);

View File

@@ -19,31 +19,31 @@ import 'package:network_proxy/utils/lang.dart';
import 'package:window_manager/window_manager.dart';
/// URI
class PathRow extends StatefulWidget {
class RequestWidget extends StatefulWidget {
final Color? color;
final HttpRequest request;
final ValueWrap<HttpResponse> response = ValueWrap();
final NetworkTabController panel;
final ProxyServer proxyServer;
final Function(PathRow)? remove;
final Function(RequestWidget)? remove;
PathRow(this.request, this.panel, {Key? key, this.color = Colors.green, required this.proxyServer, this.remove})
: super(key: GlobalKey<_PathRowState>());
RequestWidget(this.request, this.panel, {Key? key, this.color = Colors.green, required this.proxyServer, this.remove})
: super(key: GlobalKey<_RequestWidgetState>());
@override
State<PathRow> createState() => _PathRowState();
State<RequestWidget> createState() => _RequestWidgetState();
void add(HttpResponse response) {
this.response.set(response);
var state = key as GlobalKey<_PathRowState>;
var state = key as GlobalKey<_RequestWidgetState>;
state.currentState?.changeState();
}
}
class _PathRowState extends State<PathRow> {
class _RequestWidgetState extends State<RequestWidget> {
//
static _PathRowState? selectedState;
static _RequestWidgetState? selectedState;
bool selected = false;

View File

@@ -10,7 +10,7 @@ import 'package:network_proxy/ui/launch/launch.dart';
import 'package:network_proxy/utils/ip.dart';
import 'package:window_manager/window_manager.dart';
import '../left/domain.dart';
import '../left/list.dart';
class Toolbar extends StatefulWidget {
final ProxyServer proxyServer;

View File

@@ -76,8 +76,8 @@ class MobileHomeState extends State<MobileHomePage> implements EventListener, Li
}
@override
void onResponse(Channel channel, HttpResponse response) {
requestStateKey.currentState!.addResponse(channel, response);
void onResponse(ChannelContext channelContext, HttpResponse response) {
requestStateKey.currentState!.addResponse(channelContext, response);
}
@override

View File

@@ -251,7 +251,7 @@ class WriteTask extends EventListener {
void onRequest(Channel channel, HttpRequest request) {}
@override
void onResponse(Channel channel, HttpResponse response) {
void onResponse(ChannelContext channelContext, HttpResponse response) {
if (response.request == null) {
return;
}

View File

@@ -96,8 +96,7 @@ class RequestListState extends State<RequestListWidget> {
}
///添加响应
addResponse(Channel channel, HttpResponse response) {
response.request?.response = response;
addResponse(ChannelContext channelContext, HttpResponse response) {
requestSequenceKey.currentState?.addResponse(response);
domainListKey.currentState?.addResponse(response);
}
@@ -173,7 +172,6 @@ class RequestSequenceState extends State<RequestSequence> with AutomaticKeepAliv
///添加响应
addResponse(HttpResponse response) {
response.request?.response = response;
var state = indexes.remove(response.request);
state?.currentState?.change(response);

View File

@@ -437,10 +437,10 @@ packages:
dependency: transitive
description:
name: mocktail
sha256: bac151b31e4ed78bd59ab89aa4c0928f297b1180186d5daf03734519e5f596c1
sha256: f603ebd85a576e5914870b02e5839fc5d0243b867bf710651cf239a28ebb365e
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.1"
version: "1.0.2"
path:
dependency: transitive
description:
@@ -698,10 +698,10 @@ packages:
dependency: transitive
description:
name: url_launcher_linux
sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd"
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
version: "3.1.1"
url_launcher_macos:
dependency: transitive
description:
@@ -722,26 +722,26 @@ packages:
dependency: transitive
description:
name: url_launcher_web
sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7"
sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.1"
version: "2.2.2"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc"
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
version: "3.1.1"
uuid:
dependency: transitive
description:
name: uuid
sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921
sha256: bb55f38968b9427ce5dcdb8aaaa41049282195e0cfa4cf48593572fa3d1f36bc
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.2.1"
version: "4.3.1"
vector_math:
dependency: transitive
description: