http proxy init

This commit is contained in:
wanghongen
2023-06-05 19:14:21 +08:00
commit e335f8e1a3
77 changed files with 4672 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
import 'dart:async';
import '../channel.dart';
import '../handler.dart';
import '../http/codec.dart';
Future<void> main() async {
const port = 8888;
Server server = Server(port)
..initChannel((channel) {
channel.pipeline.handle(HttpRequestCodec(), HttpResponseCodec(), HttpChannelHandler());
});
await server.bind();
}

265
lib/network/channel.dart Normal file
View File

@@ -0,0 +1,265 @@
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:logger/logger.dart';
import 'package:network/network/util/AttributeKeys.dart';
import 'package:network/network/util/CertificateManager.dart';
///处理I/O事件或截获I/O操作
abstract class ChannelHandler<T> {
var log = Logger(
printer: PrettyPrinter(
methodCount: 0,
errorMethodCount: 8,
lineLength: 120,
colors: true,
printEmojis: false,
excludeBox: {Level.info: true, Level.debug: true},
));
void channelActive(Channel channel) {}
void channelRead(Channel channel, T msg) {}
void channelInactive(Channel channel) {
log.i("close $channel");
}
void exceptionCaught(Channel channel, Object cause, {StackTrace? trace}) {
var attribute = channel.getAttribute(AttributeKeys.HOST_KEY);
log.e("error $attribute $channel", cause, trace);
}
}
///与网络套接字或组件的连接能够进行读、写、连接和绑定等I/O操作。
class Channel {
final int _id;
final ChannelPipeline pipeline = ChannelPipeline();
Socket _socket;
final Map<String, Object> _attributes = {};
bool isOpen = true;
//此通道连接到的远程地址
final InternetAddress remoteAddress;
final int remotePort;
Channel(this._socket)
: _id = DateTime.now().millisecondsSinceEpoch + Random().nextInt(9999),
remoteAddress = _socket.remoteAddress,
remotePort = _socket.remotePort;
///返回此channel的全局唯一标识符。
String get id => _id.toRadixString(16);
Socket get socket => _socket;
set secureSocket(secureSocket) => _socket = secureSocket;
Future<void> write(Object obj) async {
var data = pipeline._encoder.encode(obj);
_socket.add(data);
await _socket.flush();
}
void close() {
if (isClosed) {
return;
}
_socket.destroy();
isOpen = false;
}
bool get isClosed => !isOpen;
T? getAttribute<T>(String key) {
if (!_attributes.containsKey(key)) {
return null;
}
return _attributes[key] as T;
}
void putAttribute(String key, Object value) {
_attributes[key] = value;
}
@override
String toString() {
return 'Channel($_id ${remoteAddress.host}:$remotePort)';
}
}
class ChannelPipeline extends ChannelHandler<Uint8List> {
late Decoder _decoder;
late Encoder _encoder;
late ChannelHandler _handler;
handle(Decoder decoder, Encoder encoder, ChannelHandler handler) {
_encoder = encoder;
_decoder = decoder;
_handler = handler;
}
@override
void channelActive(Channel channel) {
_handler.channelActive(channel);
}
@override
void channelRead(Channel channel, Uint8List msg) {
try {
var data = _decoder.decode(msg);
if (data != null) _handler.channelRead(channel, data!);
} catch (error, trace) {
exceptionCaught(channel, error, trace: trace);
}
}
@override
exceptionCaught(Channel channel, cause, {StackTrace? trace}) {
_handler.exceptionCaught(channel, cause, trace: trace);
}
@override
channelInactive(Channel channel) {
_handler.channelInactive(channel);
}
}
class HostAndPort {
static const String httpScheme = "http://";
static const String httpsScheme = "https://";
final String scheme;
final String host;
final int port;
HostAndPort(this.scheme, this.host, this.port);
bool isSsl() {
return httpsScheme.startsWith(scheme);
}
/// 根据url构建
static HostAndPort of(String url) {
String domain = url;
String? scheme;
//域名格式 直接解析
if (url.startsWith(httpScheme)) {
//httpScheme
scheme = url.startsWith(httpsScheme) ? httpsScheme : httpScheme;
domain = url.substring(scheme.length).split("/")[0];
}
//ip格式 host:port
List<String> hostAndPort = domain.split(":");
if (hostAndPort.length == 2) {
bool isSsl = hostAndPort[1] == "443";
scheme = isSsl ? httpsScheme : httpScheme;
return HostAndPort(scheme, hostAndPort[0], int.parse(hostAndPort[1]));
}
scheme ??= httpScheme;
return HostAndPort(scheme, hostAndPort[0], 80);
}
@override
String toString() {
return '$scheme$host:$port';
}
}
/// 解码
abstract interface class Decoder<T> {
T? decode(Uint8List data);
}
/// 编码
abstract interface class Encoder<T> {
List<int> encode(T data);
}
/// 编解码器
abstract class Codec<T> implements Decoder<T>, Encoder<T> {
static const int defaultMaxInitialLineLength = 1024;
}
class RawCodec extends Codec<Object> {
@override
Object? decode(Uint8List data) {
return data;
}
@override
List<int> encode(Object data) {
return data as List<int>;
}
}
abstract interface class ChannelInitializer {
void initChannel(Channel channel);
}
class Network {
late Function _channelInitializer;
Network initChannel(void Function(Channel channel) initializer) {
_channelInitializer = initializer;
return this;
}
Channel listen(Socket socket) {
var channel = Channel(socket);
_channelInitializer.call(channel);
channel.pipeline.channelActive(channel);
socket.listen((data) => _onEvent(data, channel),
onError: (error, trace) => channel.pipeline.exceptionCaught(channel, error, trace: trace),
onDone: () => channel.pipeline.channelInactive(channel));
return channel;
}
_onEvent(Uint8List data, Channel channel) async {
HostAndPort? hostAndPort = channel.getAttribute(AttributeKeys.HOST_KEY);
if (hostAndPort != null && hostAndPort.isSsl()) {
try {
var certificate = await CertificateManager.getCertificateContext(hostAndPort.host);
SecureSocket secureSocket = await SecureSocket.secureServer(channel.socket, certificate, bufferedData: data);
channel.secureSocket = secureSocket;
secureSocket.listen((event) => channel.pipeline.channelRead(channel, event));
} catch (error, trace) {
channel.pipeline._handler.exceptionCaught(channel, error, trace: trace);
}
return;
}
channel.pipeline.channelRead(channel, data);
}
}
class Server extends Network {
final int port;
Server(this.port);
Future<void> bind() async {
ServerSocket.bind(InternetAddress.loopbackIPv4, port).then((serverSocket) => {
serverSocket.listen((socket) {
listen(socket);
})
});
}
}
class Client extends Network {
var log = Logger();
Future<Channel> connect(HostAndPort hostAndPort) async {
if (hostAndPort.isSsl()) {
return SecureSocket.connect(hostAndPort.host, hostAndPort.port, onBadCertificate: (certificate) => true)
.then((socket) => listen(socket));
}
return Socket.connect(hostAndPort.host, hostAndPort.port).then((socket) => listen(socket));
}
}

12
lib/network/compress.dart Normal file
View File

@@ -0,0 +1,12 @@
import 'dart:io';
///GZIP 解压缩
List<int> gzipDecode(List<int> byteBuffer) {
GZipCodec gzipCodec = GZipCodec();
return gzipCodec.decode(byteBuffer);
}
///GZIP 解压缩
List<int> gzipEncode(List<int> input) {
return GZipCodec().encode(input);
}

148
lib/network/handler.dart Normal file
View File

@@ -0,0 +1,148 @@
import 'dart:collection';
import 'dart:convert';
import 'package:network/network/http/http.dart';
import 'package:network/network/http/http_headers.dart';
import 'package:network/network/util/AttributeKeys.dart';
import 'package:network/network/util/HostFilter.dart';
import 'channel.dart';
import 'http/codec.dart';
/// 获取主机和端口
HostAndPort getHostAndPort(HttpRequest request) {
String requestUri = request.uri;
//有些请求直接是路径 /xxx, 从header取host
if (request.uri.startsWith("/")) {
requestUri = request.headers.get(HttpHeaders.HOST)!;
}
return HostAndPort.of(requestUri);
}
///
class HttpChannelHandler extends ChannelHandler<HttpRequest> {
@override
void channelActive(Channel channel) {
// log.i("accept ${channel.remoteAddress.address}");
}
@override
void channelRead(Channel channel, HttpRequest msg) {
forward(channel, msg).catchError((error, trace) {
log.e("转发请求失败", error, trace);
});
}
@override
void channelInactive(Channel channel) {
super.channelInactive(channel);
Channel? remoteChannel = channel.getAttribute(channel.id);
if (remoteChannel != null) {
remoteChannel.close();
}
}
/// 转发请求
Future<void> forward(Channel channel, HttpRequest httpRequest) async {
var remoteChannel = await _getRemoteChannel(channel, httpRequest);
//实现抓包代理转发
if (httpRequest.method != HttpMethod.connect) {
if (channel.getAttribute(AttributeKeys.HOST_KEY) == null) {
remoteChannel.putAttribute(AttributeKeys.URI_KEY, httpRequest.uri);
} else {
remoteChannel.putAttribute(
AttributeKeys.URI_KEY, '${channel.getAttribute(AttributeKeys.HOST_KEY)}${httpRequest.uri}');
}
log.i("[${channel.id}] ${remoteChannel.getAttribute(AttributeKeys.URI_KEY)}");
//实现抓包代理转发
await remoteChannel.write(httpRequest);
}
}
/// 获取远程连接
Future<Channel> _getRemoteChannel(Channel clientChannel, HttpRequest httpRequest) async {
String clientId = clientChannel.id;
//客户端连接 作为缓存
Channel? remoteChannel = clientChannel.getAttribute(clientId);
if (remoteChannel != null) {
return remoteChannel;
}
var hostAndPort = getHostAndPort(httpRequest);
clientChannel.putAttribute(AttributeKeys.HOST_KEY, hostAndPort);
var proxyHandler = HttpResponseProxyHandler(clientChannel);
var proxyChannel = await HttpClients.connect(hostAndPort, proxyHandler);
//https代理新建连接请求
if (httpRequest.method == HttpMethod.connect) {
await clientChannel.write(HttpResponse(httpRequest.protocolVersion, HttpStatus.ok));
}
//黑名单
if (HostFilter.filter(hostAndPort.host)) {
relay(clientChannel, proxyChannel);
return proxyChannel;
}
clientChannel.putAttribute(clientId, proxyChannel);
return proxyChannel;
}
/// 转发请求
void relay(Channel clientChannel, Channel remoteChannel) {
var rawCodec = RawCodec();
clientChannel.pipeline.handle(rawCodec, rawCodec, RelayHandler(remoteChannel));
remoteChannel.pipeline.handle(rawCodec, rawCodec, RelayHandler(clientChannel));
}
}
/// http响应代理
class HttpResponseProxyHandler extends ChannelHandler<HttpResponse> {
final Channel clientChannel;
/// 排除的后缀 不打印日志
final Set<String> excludeContent = HashSet.from(["javascript", "text/css", "application/font-woff", "image"]);
HttpResponseProxyHandler(this.clientChannel);
@override
void channelRead(Channel channel, HttpResponse msg) {
String contentType = msg.headers.contentType;
if (excludeContent.every((element) => !contentType.contains(element))) {
log.i("[${clientChannel.id}] Response ${utf8.decode(msg.body ?? [])}");
}
//发送给客户端
clientChannel.write(msg);
}
@override
void channelInactive(Channel channel) {
super.channelInactive(channel);
clientChannel.close();
}
}
class RelayHandler extends ChannelHandler<Object> {
final Channel remoteChannel;
RelayHandler(this.remoteChannel);
@override
void channelRead(Channel channel, Object msg) {
//发送给客户端
remoteChannel.write(msg);
}
}
class HttpClients {
/// 建立连接
static Future<Channel> connect(HostAndPort hostAndPort, ChannelHandler<HttpResponse> handler) async {
var client = Client()
..initChannel((channel) => channel.pipeline.handle(HttpResponseCodec(), HttpRequestCodec(), handler));
return client.connect(hostAndPort);
}
}

312
lib/network/http/codec.dart Normal file
View File

@@ -0,0 +1,312 @@
import 'dart:math';
import 'dart:typed_data';
import 'package:network/utils/num.dart';
import '../channel.dart';
import '../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;
}
enum State {
readInitial,
readHeader,
readVariableLengthContent,
readFixedLengthContent,
readChunkedContent,
done,
}
/// http编解码
abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
final HttpParse _httpParse = HttpParse();
State _state = State.readInitial;
///chunked编码 剩余未读取的chunk大小
int _chunkReadableSize = 0;
late T message;
final BytesBuilder _bodyBuffer = BytesBuilder();
T createMessage(List<String> reqLine);
@override
T? decode(Uint8List data) {
//请求行
if (_state == State.readInitial) {
var initialLine = _readInitialLine(data);
message = createMessage(initialLine);
_state = State.readHeader;
}
//请求头
if (_state == State.readHeader) {
_readHeader(data, message);
_state = message.headers.isChunked ? State.readVariableLengthContent : State.readFixedLengthContent;
}
if (message is HttpRequest) {
if ([HttpMethod.get, HttpMethod.connect].contains((message as HttpRequest).method)) {
_state = State.done;
}
}
//chunked编码
if (_state == State.readChunkedContent) {
_bodyBuffer.add(data.sublist(0, min(_chunkReadableSize, data.length)));
if (data.length < _chunkReadableSize) {
_chunkReadableSize = _chunkReadableSize - data.length;
return null;
}
_httpParse.index = _chunkReadableSize + 2;
_state = State.readVariableLengthContent; //读取下一个chunk
}
//请求体
if (_state == State.readFixedLengthContent || _state == State.readVariableLengthContent) {
_readBody(data, message);
if ((message.contentLength == -1 && !message.headers.isChunked) || _bodyBuffer.length == message.contentLength) {
_state = State.done;
}
}
if (_state == State.done) {
message.body = _convertBody();
_state = State.readInitial;
return message;
}
return null;
}
void initialLine(BytesBuilder buffer, T message);
@override
List<int> encode(T message) {
BytesBuilder builder = BytesBuilder();
//请求行
initialLine(builder, message);
if (message.headers.isGzip) {
message.body = gzipEncode(message.body!);
}
//请求头
message.headers.remove(HttpHeaders.TRANSFER_ENCODING);
int contentLength = _contentLength(message);
message.headers.contentLength = contentLength;
message.headers.forEach((key, value) {
builder
..add(key.codeUnits)
..addByte(HttpConstants.colon)
..addByte(HttpConstants.sp)
..add(value.codeUnits)
..addByte(HttpConstants.cr)
..addByte(HttpConstants.lf);
});
builder.addByte(HttpConstants.cr);
builder.addByte(HttpConstants.lf);
//请求体
builder.add(message.body ?? Uint8List(0));
return builder.toBytes();
}
int _contentLength(T message) {
return message.body?.length ?? 0;
}
//读取起始行
List<String> _readInitialLine(Uint8List data) {
_httpParse.reset();
int maxSize = min(data.length, Codec.defaultMaxInitialLineLength);
return _httpParse.parseInitialLine(data, maxSize);
}
//读取请求头
void _readHeader(Uint8List data, T message) {
_httpParse.parseHeader(data, message.headers);
message.contentLength = message.headers.contentLength;
}
//读取请求体
void _readBody(Uint8List data, T message) {
if (_state == State.readVariableLengthContent) {
while (_httpParse.index < data.length) {
var parseLine = _httpParse.parseLine(data);
if (parseLine.isEmpty) {
continue;
}
int length = hexToInt(String.fromCharCodes(parseLine));
//chunked编码结束
if (length == 0) {
_state = State.done;
return;
}
_bodyBuffer.add(data.sublist(_httpParse.index, min(_httpParse.index + length, data.length)));
if (_httpParse.index + length > data.length) {
_state = State.readChunkedContent;
_chunkReadableSize = length - (data.length - _httpParse.index);
}
_httpParse.index += length + 2; //跳过\r\n
}
_httpParse.index = 0;
} else if (message.contentLength > 0) {
_bodyBuffer.add(data.sublist(_httpParse.index));
_httpParse.index = 0;
}
}
//转换body
List<int> _convertBody() {
List<int> bytes = _bodyBuffer.toBytes();
if (message.headers.isGzip) {
bytes = gzipDecode(bytes);
}
_bodyBuffer.clear();
return bytes;
}
}
/// http请求编解码
class HttpRequestCodec extends HttpCodec<HttpRequest> {
@override
HttpRequest createMessage(List<String> reqLine) {
HttpMethod httpMethod = HttpMethod.valueOf(reqLine[0]);
return HttpRequest(httpMethod, reqLine[1], reqLine[2]);
}
@override
void initialLine(BytesBuilder buffer, HttpRequest message) {
//请求行
buffer
..add(message.method.name.codeUnits)
..addByte(HttpConstants.sp)
..add(message.uri.codeUnits)
..addByte(HttpConstants.sp)
..add(message.protocolVersion.codeUnits)
..addByte(HttpConstants.cr)
..addByte(HttpConstants.lf);
}
}
/// http响应编解码
class HttpResponseCodec extends HttpCodec<HttpResponse> {
@override
HttpResponse createMessage(List<String> reqLine) {
var httpStatus = HttpStatus(int.parse(reqLine[1]), reqLine[2]);
return HttpResponse(reqLine[0], httpStatus);
}
@override
void initialLine(BytesBuilder buffer, HttpResponse message) {
//状态行
buffer.add(message.protocolVersion.codeUnits);
buffer.addByte(HttpConstants.sp);
buffer.add(message.status.code.toString().codeUnits);
buffer.addByte(HttpConstants.sp);
buffer.add(message.status.reasonPhrase.codeUnits);
buffer.addByte(HttpConstants.cr);
buffer.addByte(HttpConstants.lf);
}
}
/// http解析器
class HttpParse {
int index = 0;
/// 解析请求行
List<String> parseInitialLine(Uint8List data, int size) {
List<String> initialLine = [];
for (int i = index; i < size; i++) {
if (_isLineEnd(data, i)) {
//请求行结束
Uint8List requestLine = data.sublist(index, i - 1);
initialLine = _splitLine(requestLine);
index = i + 1;
break;
}
}
if (initialLine.length != 3) {
throw Exception("parseLine error ${String.fromCharCodes(data)}");
}
return initialLine;
}
/// 解析请求头
void parseHeader(Uint8List data, HttpHeaders headers) {
while (true) {
var line = parseLine(data);
if (line.isEmpty) {
break;
}
var header = _splitHeader(line);
headers.set(header[0], header[1]);
}
}
Uint8List parseLine(Uint8List data) {
for (int i = index; i < data.length; i++) {
if (_isLineEnd(data, i)) {
var line = data.sublist(index, i - 1);
index = i + 1;
return line;
}
}
return Uint8List(0);
}
void reset() {
index = 0;
}
//是否行结束
bool _isLineEnd(List<int> data, int index) {
return index >= 1 && data[index] == HttpConstants.lf && data[index - 1] == HttpConstants.cr;
}
//分割行
List<String> _splitLine(Uint8List data) {
List<String> lines = [];
int start = 0;
for (int i = 0; i < data.length; i++) {
if (data[i] == HttpConstants.sp) {
lines.add(String.fromCharCodes(data.sublist(start, i)));
start = i + 1;
if (lines.length == 2) {
break;
}
}
}
lines.add(String.fromCharCodes(data.sublist(start)));
return lines;
}
//分割头
List<String> _splitHeader(List<int> data) {
List<String> headers = [];
for (int i = 0; i < data.length; i++) {
if (data[i] == HttpConstants.colon && data[i + 1] == HttpConstants.sp) {
headers.add(String.fromCharCodes(data.sublist(0, i)));
headers.add(String.fromCharCodes(data.sublist(i + 2)));
break;
}
}
return headers;
}
}

139
lib/network/http/http.dart Normal file
View File

@@ -0,0 +1,139 @@
import 'http_headers.dart';
///定义HTTP消息的接口为HttpRequest和HttpResponse提供公共属性。
abstract class HttpMessage {
final String protocolVersion;
final HttpHeaders headers = HttpHeaders();
int contentLength = -1;
List<int>? body;
HttpMessage(this.protocolVersion);
String get bodyAsString {
if (body == null) {
return "";
}
return String.fromCharCodes(body!);
}
}
///HTTP请求。
class HttpRequest extends HttpMessage {
final String uri;
late HttpMethod method;
HttpRequest(this.method, this.uri, String protocolVersion)
: super(protocolVersion);
@override
String toString() {
return 'HttpReqeust{version: $protocolVersion, url: $uri, method: ${method.name}, headers: $headers, contentLength: $contentLength, bodyLength: ${body?.length}}';
}
}
///HTTP响应。
class HttpResponse extends HttpMessage {
final HttpStatus status;
HttpResponse(String protocolVersion, this.status) : super(protocolVersion);
@override
String toString() {
return 'HttpResponse{status: ${status.code}, headers: $headers, contentLength: $contentLength, bodyLength: ${body?.length}}';
}
}
///HTTP请求方法。
enum HttpMethod {
options("OPTIONS"),
get("GET"),
head("HEAD"),
post("POST"),
put("PUT"),
patch("PATCH"),
delete("DELETE"),
trace("TRACE"),
connect("CONNECT");
final String name;
const HttpMethod(this.name);
static HttpMethod valueOf(String name) {
return HttpMethod.values
.firstWhere((element) => element.name == name.toUpperCase());
}
}
///HTTP响应状态。
class HttpStatus {
/// 200 OK
static final HttpStatus ok = newStatus(200, "OK");
/// 400 Bad Request
static final HttpStatus badRequest = newStatus(400, "Bad Request");
/// 401 Unauthorized
static final HttpStatus unauthorized = newStatus(401, "Unauthorized");
/// 403 Forbidden
static final HttpStatus forbidden = newStatus(403, "Forbidden");
/// 404 Not Found
static final HttpStatus notFound = newStatus(404, "Not Found");
/// 500 Internal Server Error
static final HttpStatus internalServerError =
newStatus(500, "Internal Server Error");
/// 502 Bad Gateway
static final HttpStatus badGateway = newStatus(502, "Bad Gateway");
/// 503 Service Unavailable
static final HttpStatus serviceUnavailable =
newStatus(503, "Service Unavailable");
/// 504 Gateway Timeout
static final HttpStatus gatewayTimeout =
newStatus(504, "Gateway Timeout");
static HttpStatus newStatus(int statusCode, String reasonPhrase) {
return HttpStatus(statusCode, reasonPhrase);
}
static HttpStatus? valueOf(int code) {
switch (code) {
case 200:
return ok;
case 400:
return badRequest;
case 401:
return unauthorized;
case 403:
return forbidden;
case 404:
return notFound;
case 500:
return internalServerError;
case 502:
return badGateway;
case 503:
return serviceUnavailable;
case 504:
return gatewayTimeout;
}
return null;
}
final int code;
final String reasonPhrase;
HttpStatus(this.code, this.reasonPhrase);
@override
String toString() {
return 'HttpResponseStatus{code: $code, reasonPhrase: $reasonPhrase}';
}
}

View File

@@ -0,0 +1,66 @@
import 'dart:collection';
class HttpHeaders {
static const CONTENT_LENGTH = "Content-Length";
static const CONTENT_ENCODING = "Content-Encoding";
static const CONTENT_TYPE = "Content-Type";
static const String HOST = "Host";
static const String TRANSFER_ENCODING = "Transfer-Encoding";
final LinkedHashMap<String, String> _headers = LinkedHashMap<String, String>();
// 由小写标头名称键入的原始标头名称。
final Map<String, String> _originalHeaderNames = {};
///设置header。
void set(String name, String value) {
_headers[name.toLowerCase()] = value;
_originalHeaderNames[name] = value;
}
String? get(String name) {
return _headers[name.toLowerCase()];
}
void remove(String name) {
_headers.remove(name.toLowerCase());
_originalHeaderNames.remove(name);
_originalHeaderNames.remove(name.toLowerCase());
}
int? getInt(String name) {
final value = get(name);
if (value == null) {
return null;
}
return int.parse(value);
}
bool getBool(String name) {
final value = get(name);
if (value == null) {
return false;
}
return value.toLowerCase() == "true";
}
int get contentLength => getInt(CONTENT_LENGTH) ?? -1;
set contentLength(int contentLength) => set(CONTENT_LENGTH, contentLength.toString());
bool get isGzip => get(HttpHeaders.CONTENT_ENCODING) == "gzip";
bool get isChunked => get(HttpHeaders.TRANSFER_ENCODING) == "chunked";
void forEach(void Function(String name, String value) f) {
_originalHeaderNames.forEach(f);
}
set contentType(String contentType) => set(CONTENT_TYPE, contentType);
String get contentType => get(CONTENT_TYPE) ?? "";
@override
String toString() {
return 'HttpHeaders{$_originalHeaderNames}';
}
}

View File

@@ -0,0 +1,7 @@
/// @author wanghongen
/// 2023/5/23
interface class AttributeKeys {
static String HOST_KEY = "HOST";
static String URI_KEY = "URI";
static String REQUEST_KEY = "REQUEST";
}

View File

@@ -0,0 +1,82 @@
import 'dart:core';
import 'dart:io';
import 'package:basic_utils/basic_utils.dart';
Future<void> main() async {
var securityContext = await CertificateManager.getCertificateContext('www.baidu.com');
print(securityContext);
// print(CertificateManager.caCert.tbsCertificateSeqAsString);
print(CertificateManager._caCert.tbsCertificate?.subject);
print(CertificateManager._caCert.tbsCertificateSeqAsString);
print(CertificateManager._caCert);
}
class CertificateManager {
/// 证书缓存
static final Map<String, SecurityContext> _certificateMap = {};
/// 服务端密钥
static final AsymmetricKeyPair _serverKeyPair = CryptoUtils.generateRSAKeyPair();
/// ca证书
static late X509CertificateData _caCert;
/// ca私钥
static late RSAPrivateKey _caPriKey;
/// 是否初始化
static bool _initialized = false;
/// 获取域名自签名证书
static Future<SecurityContext> getCertificateContext(String host) async {
if (_certificateMap.containsKey(host)) {
return _certificateMap[host]!;
}
if (!_initialized) {
await _initCAConfig();
}
var cer = generate(_serverKeyPair.publicKey as RSAPublicKey, _caPriKey, host);
var rsaPrivateKey = _serverKeyPair.privateKey as RSAPrivateKey;
var securityContext = SecurityContext.defaultContext
..useCertificateChainBytes(cer.codeUnits)
..usePrivateKeyBytes(CryptoUtils.encodeRSAPrivateKeyToPemPkcs1(rsaPrivateKey).codeUnits);
_certificateMap[host] = securityContext;
return securityContext;
}
/// 生成证书
static String generate(PublicKey serverPubKey, RSAPrivateKey caPriKey, String host) {
//根据CA证书subject来动态生成目标服务器证书的issuer和subject
Map<String, String> x509Subject = {
'C': 'CN',
'ST': 'BJ',
'L': 'BJ',
'O': 'network',
'OU': 'Proxy',
};
x509Subject['CN'] = host;
var csr = X509Utils.generateRsaCsrPem(x509Subject, caPriKey, serverPubKey as RSAPublicKey, san: [host]);
Map<String, String> issuer = Map.from(_caCert.tbsCertificate!.subject);
var csrPem = X509Utils.generateSelfSignedCertificate(caPriKey, csr, 3650, sans: [host], issuer: issuer);
return csrPem;
}
static Future<void> _initCAConfig() async {
if (_initialized) {
return;
}
//从项目目录加入ca根证书
var caPem = await File('assets/certs/ca.crt').readAsString();
_caCert = X509Utils.x509CertificateFromPem(caPem);
//根据CA证书subject来动态生成目标服务器证书的issuer和subject
//从项目目录加入ca私钥
var privateBytes = await File('assets/certs/ca_private.der').readAsBytes();
_caPriKey = CryptoUtils.rsaPrivateKeyFromDERBytes(privateBytes);
_initialized = true;
}
}

View File

@@ -0,0 +1,53 @@
import 'dart:collection';
void main() {
print(HostFilter.filter("www.apple.com"));
}
class HostFilter {
/// 白名单
static final Set<RegExp> _whitelist = buildWhitelist();
/// 黑名单
static final Set<RegExp> _blacklist = buildBlacks();
/// 构建白名单
static Set<RegExp> buildWhitelist() {
List<String> whites = [];
// whites.add("*.google.com");
// whites.add("www.baidu.com");
Set<RegExp> whitelist = HashSet<RegExp>();
for (var white in whites) {
whitelist.add(RegExp(white));
}
return whitelist;
}
/// 构建黑名单
static Set<RegExp> buildBlacks() {
List<String> blacks = [];
blacks.add(r"*.google.*");
blacks.add(r"*\.github\.com");
blacks.add(r"*.apple.*");
blacks.add(r"*.qq.com");
// blacks.add(r"www.baidu.com");
Set<RegExp> blacklist = HashSet<RegExp>();
for (var black in blacks) {
blacklist.add(RegExp(black.replaceAll("*", ".*")));
}
return blacklist;
}
/// 是否过滤
static bool filter(String host) {
//如果白名单不为空,不在白名单里都是黑名单
if (_whitelist.isNotEmpty) {
return _whitelist.any((element) => !element.hasMatch(host));
}
return _blacklist.any((element) => element.hasMatch(host));
}
}