mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-03-31 07:19:46 +08:00
307 lines
7.4 KiB
Dart
307 lines
7.4 KiB
Dart
import 'dart:math';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:network_proxy/network/http/body_reader.dart';
|
|
|
|
import '../../utils/compress.dart';
|
|
import '../channel.dart';
|
|
import '../util/logger.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,
|
|
body,
|
|
done,
|
|
}
|
|
|
|
/// http编解码
|
|
abstract class HttpCodec<T extends HttpMessage> implements Codec<T> {
|
|
final HttpParse _httpParse = HttpParse();
|
|
State _state = State.readInitial;
|
|
|
|
late T message;
|
|
|
|
BodyReader? bodyReader;
|
|
|
|
T createMessage(List<String> reqLine);
|
|
|
|
@override
|
|
T? decode(Uint8List data) {
|
|
_httpParse.index = 0;
|
|
|
|
//请求行
|
|
if (_state == State.readInitial) {
|
|
init();
|
|
try {
|
|
var initialLine = _readInitialLine(data);
|
|
message = createMessage(initialLine);
|
|
_state = State.readHeader;
|
|
} catch (e, cause) {
|
|
logger.e("解析请求行失败 [${String.fromCharCodes(data)}]", e, cause);
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
//请求头
|
|
if (_state == State.readHeader) {
|
|
_readHeader(data, message);
|
|
}
|
|
|
|
//请求体
|
|
if (_state == State.body) {
|
|
var result = bodyReader!.readBody(data.sublist(_httpParse.index));
|
|
if (result.isDone) {
|
|
_state = State.done;
|
|
message.body = result.body;
|
|
}
|
|
}
|
|
|
|
if (_state == State.done) {
|
|
message.body = _convertBody(message.body);
|
|
_state = State.readInitial;
|
|
return message;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
void init() {
|
|
_httpParse.reset();
|
|
bodyReader = null;
|
|
}
|
|
|
|
void initialLine(BytesBuilder buffer, T message);
|
|
|
|
@override
|
|
List<int> encode(T message) {
|
|
BytesBuilder builder = BytesBuilder();
|
|
//请求行
|
|
initialLine(builder, message);
|
|
|
|
List<int>? body = message.body;
|
|
if (message.headers.isGzip) {
|
|
body = gzipEncode(body!);
|
|
}
|
|
|
|
//请求头
|
|
message.headers.remove(HttpHeaders.TRANSFER_ENCODING);
|
|
if (body != null && body.isNotEmpty) {
|
|
message.headers.contentLength = body.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(Uint8List data) {
|
|
int maxSize = min(data.length, Codec.defaultMaxInitialLineLength);
|
|
return _httpParse.parseInitialLine(data, maxSize);
|
|
}
|
|
|
|
//读取请求头
|
|
void _readHeader(Uint8List data, T message) {
|
|
if (_httpParse.parseHeader(data, message.headers)) {
|
|
message.contentLength = message.headers.contentLength;
|
|
_state = State.body;
|
|
bodyReader = BodyReader(message);
|
|
}
|
|
}
|
|
|
|
//转换body
|
|
List<int>? _convertBody(List<int>? bytes) {
|
|
if (bytes == null) {
|
|
return null;
|
|
}
|
|
if (message.headers.isGzip) {
|
|
bytes = gzipDecode(bytes);
|
|
}
|
|
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) {
|
|
//请求行
|
|
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;
|
|
BytesBuilder inBytes = BytesBuilder();
|
|
|
|
/// 解析请求行
|
|
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;
|
|
}
|
|
|
|
/// 解析请求头
|
|
bool parseHeader(Uint8List data, HttpHeaders headers) {
|
|
if (inBytes.length > Codec.defaultMaxInitialLineLength) {
|
|
inBytes.clear();
|
|
throw Exception("header too long");
|
|
}
|
|
|
|
while (true) {
|
|
Uint8List line = Uint8List(0);
|
|
for (int i = index; i < data.length; i++) {
|
|
if (_isLineEnd(data, i)) {
|
|
line = data.sublist(index, i - 1);
|
|
index = i + 1;
|
|
break;
|
|
}
|
|
if (i == data.length - 1) {
|
|
inBytes.add(data.sublist(index, i + 1));
|
|
index = i + 1;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (line.isEmpty) {
|
|
break;
|
|
}
|
|
|
|
if (inBytes.isNotEmpty) {
|
|
inBytes.add(line);
|
|
line = inBytes.toBytes();
|
|
inBytes.clear();
|
|
}
|
|
var header = _splitHeader(line);
|
|
headers.add(header[0], header[1]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|