mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-03-17 04:43:17 +08:00
301 lines
8.5 KiB
Dart
301 lines
8.5 KiB
Dart
/*
|
|
* Copyright 2023 Hongen Wang All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* https://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import 'dart:math';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:proxypin/network/channel/channel_context.dart';
|
|
import 'package:proxypin/network/channel/host_port.dart';
|
|
import 'package:proxypin/network/http/parse/body_reader.dart';
|
|
import 'package:proxypin/network/http/constants.dart';
|
|
import 'package:proxypin/network/http/h2/h2_codec.dart';
|
|
import 'package:proxypin/network/http/parse/http_parser.dart';
|
|
import 'package:proxypin/network/util/byte_buf.dart';
|
|
|
|
import 'http.dart';
|
|
import 'http_headers.dart';
|
|
|
|
class ParserException implements Exception {
|
|
final String message;
|
|
final String? source;
|
|
|
|
ParserException(this.message, [this.source]);
|
|
|
|
@override
|
|
String toString() {
|
|
return 'ParserException{message: $message source: $source}';
|
|
}
|
|
}
|
|
|
|
enum State {
|
|
readInitial,
|
|
readHeader,
|
|
body,
|
|
done,
|
|
}
|
|
|
|
class DecoderResult<T> {
|
|
bool isDone = true;
|
|
T? data;
|
|
bool supportedParse;
|
|
|
|
//转发消息
|
|
List<int>? forward;
|
|
|
|
DecoderResult({this.isDone = true, this.supportedParse = true});
|
|
}
|
|
|
|
/// 解码
|
|
abstract interface class Decoder<T> {
|
|
/// 解码 如果返回null说明数据不完整
|
|
DecoderResult<T> decode(ChannelContext channelContext, ByteBuf byteBuf);
|
|
}
|
|
|
|
/// 编码
|
|
abstract interface class Encoder<T> {
|
|
List<int> encode(ChannelContext channelContext, T data);
|
|
}
|
|
|
|
/// 编解码器
|
|
abstract class Codec<D, E> implements Decoder<D>, Encoder<E> {
|
|
static const int defaultMaxInitialLineLength = 1024000; // 1M
|
|
static const int maxBodyLength = 4096000; // 4M
|
|
}
|
|
|
|
/// http编解码
|
|
abstract class HttpCodec<T extends HttpMessage> implements Codec<T, T> {
|
|
final HttpParse _httpParse = HttpParse();
|
|
Http2Codec<T>? _h2Codec;
|
|
State _state = State.readInitial;
|
|
|
|
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
|
|
DecoderResult<T> decode(ChannelContext channelContext, ByteBuf data) {
|
|
var protocol = channelContext.clientChannel?.selectedProtocol;
|
|
|
|
if (protocol == HttpConstants.h2 || protocol == HttpConstants.h2_14) {
|
|
return getH2Codec().decode(channelContext, data);
|
|
}
|
|
|
|
//请求行
|
|
if (_state == State.readInitial) {
|
|
init();
|
|
var initialLine = _readInitialLine(data);
|
|
if (initialLine.isEmpty) {
|
|
return result;
|
|
}
|
|
result.data = createMessage(initialLine);
|
|
_state = State.readHeader;
|
|
}
|
|
|
|
//请求头
|
|
try {
|
|
if (_state == State.readHeader) {
|
|
_readHeader(data, result.data!);
|
|
}
|
|
|
|
//请求体
|
|
if (_state == State.body) {
|
|
bool resolveBody = channelContext.currentRequest?.method != HttpMethod.head;
|
|
var bodyResult = resolveBody ? bodyReader!.readBody(data.readAvailableBytes()) : null;
|
|
if (!resolveBody || bodyResult?.isDone == true) {
|
|
_state = State.done;
|
|
result.data!.body = bodyResult?.body;
|
|
}
|
|
|
|
//If the body does not support parsing, forward directly
|
|
if (bodyResult != null && !bodyResult.supportedParse) {
|
|
result.supportedParse = false;
|
|
result.forward = bodyResult.body;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (_state == State.done) {
|
|
result.data!.body = _convertBody(result.data!.body);
|
|
_state = State.readInitial;
|
|
result.isDone = true;
|
|
return result;
|
|
}
|
|
} catch (e) {
|
|
_state = State.readInitial;
|
|
rethrow;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void init() {
|
|
bodyReader = null;
|
|
result = DecoderResult(isDone: false);
|
|
}
|
|
|
|
void initialLine(BytesBuilder buffer, T message);
|
|
|
|
@override
|
|
List<int> encode(ChannelContext channelContext, T message) {
|
|
if (message.protocolVersion == "HTTP/2") {
|
|
return getH2Codec().encode(channelContext, message);
|
|
}
|
|
|
|
BytesBuilder builder = BytesBuilder();
|
|
//请求行
|
|
initialLine(builder, message);
|
|
|
|
List<int>? body = message.body;
|
|
|
|
//请求头
|
|
bool isChunked = message.headers.isChunked;
|
|
message.headers.remove(HttpHeaders.TRANSFER_ENCODING);
|
|
|
|
if (body != null && (body.isNotEmpty || isChunked)) {
|
|
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
|
|
..add(key.codeUnits)
|
|
..addByte(HttpConstants.colon)
|
|
..addByte(HttpConstants.sp)
|
|
..add(v.codeUnits)
|
|
..addByte(HttpConstants.cr)
|
|
..addByte(HttpConstants.lf);
|
|
}
|
|
});
|
|
builder.addByte(HttpConstants.cr);
|
|
builder.addByte(HttpConstants.lf);
|
|
|
|
//请求体
|
|
builder.add(body ?? Uint8List(0));
|
|
return builder.toBytes();
|
|
}
|
|
|
|
//读取起始行
|
|
List<String> _readInitialLine(ByteBuf data) {
|
|
int maxSize = min(data.readableBytes(), Codec.defaultMaxInitialLineLength);
|
|
return _httpParse.parseInitialLine(data, maxSize);
|
|
}
|
|
|
|
//读取请求头
|
|
void _readHeader(ByteBuf data, T message) {
|
|
if (_httpParse.parseHeaders(data, message.headers)) {
|
|
_state = State.body;
|
|
bodyReader = BodyReader(message);
|
|
}
|
|
}
|
|
|
|
//转换body
|
|
List<int>? _convertBody(List<int>? bytes) {
|
|
if (bytes == null) {
|
|
return null;
|
|
}
|
|
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], protocolVersion: reqLine[2]);
|
|
}
|
|
|
|
@override
|
|
void initialLine(BytesBuilder buffer, HttpRequest message) {
|
|
String uri = message.uri;
|
|
|
|
//http scheme 输入地址和host不一致
|
|
if (uri.startsWith(HostAndPort.httpScheme) &&
|
|
(message.requestUri?.host != message.headers.host && message.headers.host?.contains(':') != true)) {
|
|
uri = message.requestUri?.replace(host: message.headers.host).toString() ?? uri;
|
|
}
|
|
|
|
//请求行
|
|
buffer
|
|
..add(message.method.name.codeUnits)
|
|
..addByte(HttpConstants.sp)
|
|
..add(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(httpStatus, protocolVersion: reqLine[0]);
|
|
}
|
|
|
|
@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);
|
|
}
|
|
}
|
|
|
|
class HttpServerCodec extends Codec<HttpRequest, HttpResponse> {
|
|
HttpRequestCodec requestCodec = HttpRequestCodec();
|
|
HttpResponseCodec responseCodec = HttpResponseCodec();
|
|
|
|
@override
|
|
DecoderResult<HttpRequest> decode(ChannelContext channelContext, ByteBuf byteBuf) {
|
|
return requestCodec.decode(channelContext, byteBuf);
|
|
}
|
|
|
|
@override
|
|
List<int> encode(ChannelContext channelContext, HttpResponse data) {
|
|
return responseCodec.encode(channelContext, data);
|
|
}
|
|
}
|
|
|
|
class HttpClientCodec extends Codec<HttpResponse, HttpRequest> {
|
|
HttpRequestCodec requestCodec = HttpRequestCodec();
|
|
HttpResponseCodec responseCodec = HttpResponseCodec();
|
|
|
|
@override
|
|
DecoderResult<HttpResponse> decode(ChannelContext channelContext, ByteBuf byteBuf) {
|
|
return responseCodec.decode(channelContext, byteBuf);
|
|
}
|
|
|
|
@override
|
|
List<int> encode(ChannelContext channelContext, HttpRequest data) {
|
|
return requestCodec.encode(channelContext, data);
|
|
}
|
|
}
|