Dart代理隧道连接Binance API:静态代理失效问题排查
Dart HTTP代理隧道连接Binance API静态代理无响应问题
我编写了一段Dart代码,用于通过HTTP代理服务器创建隧道连接Binance API。代码使用动态代理时可正常发起请求(如packetstream.io提供的代理),但使用静态代理时无响应。预期能收到Binance API的响应及响应头,但静态代理场景下无返回,尝试多个服务商的静态代理均出现该问题,请问代码中存在哪些错误?
void main() async { final uri = Uri.parse( 'https://api.binance.com/sapi/v1/asset/get-funding-asset'); final transport = await connectTunnel('185.142.98.232', 29130, uri.host, username: 'Pbt5MoAU89ph', password: 'itsallgood.man'); final payload = jsonEncode({'key': 'value'}); final contentLength = utf8.encode(payload).length; final headers = [ Header.ascii(':method', 'POST'), Header.ascii(':path', uri.hasQuery ? uri.path + '?' + uri.query : uri.path), Header.ascii(':scheme', uri.scheme), Header.ascii(':authority', uri.host), Header.ascii('content-type', 'application/json'), Header.ascii('content-length', contentLength.toString()), ]; final stream = transport.makeRequest(headers, endStream: false); stream.outgoingMessages.add(DataStreamMessage(utf8.encode(payload))); await stream.outgoingMessages.close(); final bodyStream = StreamController<List<int>>(); utf8.decoder.bind(bodyStream.stream).listen(print); await for (final message in stream.incomingMessages) { if (message is HeadersStreamMessage) { for (final header in message.headers) { final name = utf8.decode(header.name); final value = utf8.decode(header.value); print('Header: $name: $value'); } } else if (message is DataStreamMessage) { bodyStream.add(message.bytes); if (message.endStream) { await bodyStream.close(); } } else { print('Incoming message: $message'); } } if (!bodyStream.isClosed) { await bodyStream.close(); } await transport.finish(); } Future<ClientTransportConnection> connectTunnel( String proxyHost, int proxyPort, String targetHost, { required String username, required String password, int targetPort = 443, }) async { try { final proxy = await Socket.connect( proxyHost, proxyPort, timeout: const Duration(seconds: 10), ); const CRLF = '\r\n'; proxy ..write('CONNECT $targetHost:$targetPort HTTP/1.1') // request line ..write(CRLF); proxy.write('Host: $targetHost:$targetPort'); // header proxy.write(CRLF); final authString = base64.encode(utf8.encode('$username:$password')); proxy.write('Proxy-Authorization: Basic $authString'); proxy.write(CRLF); proxy.write(CRLF); final completer = Completer<bool>.sync(); final sub = proxy.listen( (event) { final response = ascii.decode(event); final lines = response.split(CRLF); // status line final statusLine = lines.first; if (statusLine.startsWith('HTTP/1.1 200')) { print(statusLine); completer.complete(true); } else { completer.completeError(statusLine); } }, onError: completer.completeError, ); await completer.future; // established sub.pause(); // ignore: close_sinks final socket = await SecureSocket.secure( proxy, host: targetHost, supportedProtocols: const ['h2'], ); // ignore: newline-before-return return ClientTransportConnection.viaSocket(socket); } on SocketException catch (e) { print('SocketException: $e'); rethrow; } on HttpException catch (e) { print('HttpException: $e'); rethrow; } catch (e) { print('Error: $e'); rethrow; } }
代码中的错误点
1. 代理响应残留数据未清理
静态代理返回的完整响应(包括状态行、响应头和结尾空行)会留在Socket缓冲区中,当前代码仅检查状态行就暂停监听,后续TLS握手时会把这些HTTP数据当成TLS协议数据,导致握手失败、连接卡住。
修复方法:确认隧道建立后,读取并丢弃所有代理响应数据直到空行:
// 在await completer.future;之后添加 const CRLF = '\r\n'; StringBuffer responseBuffer = StringBuffer(); while (true) { List<int> data = proxy.read(); if (data.isEmpty) break; responseBuffer.write(ascii.decode(data)); if (responseBuffer.toString().endsWith('$CRLF$CRLF')) { break; } } sub.cancel(); // 取消原监听
2. 强制指定HTTP/2协议
代码中SecureSocket.secure强制要求使用HTTP/2,但部分静态代理或目标服务可能不支持该协议,导致连接建立后无法正常通信。
修复方法:移除强制HTTP/2的配置,让系统自动协商支持的协议:
final socket = await SecureSocket.secure( proxy, host: targetHost, // 移除supportedProtocols参数 );
3. 代理响应处理不完整
当前监听逻辑仅处理单次接收的数据,若代理响应分多次发送,会导致状态行解析不完整,无法触发隧道建立的完成逻辑。
修复方法:累积数据直到收到完整的HTTP响应头(以空行结尾):
// 替换原代理监听逻辑 final completer = Completer<bool>.sync(); StringBuffer responseBuffer = StringBuffer(); final sub = proxy.listen( (event) { responseBuffer.write(ascii.decode(event)); if (responseBuffer.toString().contains('$CRLF$CRLF')) { final response = responseBuffer.toString(); final lines = response.split(CRLF); final statusLine = lines.first; if (statusLine.startsWith(RegExp(r'HTTP/\d\.\d 200'))) { print(statusLine); completer.complete(true); } else { print('Proxy error response: $response'); completer.completeError(statusLine); } } }, onError: completer.completeError, );
4. 状态码检查过严格
静态代理可能返回HTTP/1.0 200等其他成功状态码,当前代码仅匹配HTTP/1.1 200会导致误判。
修复方法:放宽状态码匹配规则,同时输出完整错误响应便于排查:
if (statusLine.startsWith(RegExp(r'HTTP/\d\.\d 200'))) { print(statusLine); completer.complete(true); } else { print('Proxy error response: ${responseBuffer.toString()}'); completer.completeError(statusLine); }
内容的提问来源于stack exchange,提问作者Neon RolePlay
相关产品推荐
相关产品推荐

