84 lines
2.2 KiB
Dart
84 lines
2.2 KiB
Dart
// Client for forwarding requests to k_server (:8780).
|
|
// Mirrors the k_proxy → k_server leg in k_proxy_app.py.
|
|
|
|
import 'dart:io';
|
|
import 'dart:typed_data';
|
|
|
|
const String kServerHost = '127.0.0.1'; // k_server address (same device or Qubes forward)
|
|
const int kServerPort = 8780;
|
|
|
|
class KServerResponse {
|
|
final int statusCode;
|
|
final HttpHeaders headers;
|
|
final Uint8List body;
|
|
|
|
KServerResponse({
|
|
required this.statusCode,
|
|
required this.headers,
|
|
required this.body,
|
|
});
|
|
}
|
|
|
|
class KServerClient {
|
|
HttpClient? _client;
|
|
|
|
HttpClient _getClient() {
|
|
// TLS: k_server uses self-signed cert from generate_phase2_certs.py.
|
|
// In dev, accept any cert; in prod, pin the CA cert.
|
|
_client ??= HttpClient()
|
|
..badCertificateCallback = (cert, host, port) {
|
|
// TODO: replace with CA pinning once certs are bundled.
|
|
return true;
|
|
};
|
|
return _client!;
|
|
}
|
|
|
|
Future<KServerResponse> forward({
|
|
required String method,
|
|
required String path,
|
|
required HttpHeaders headers,
|
|
required Uint8List body,
|
|
}) async {
|
|
final client = _getClient();
|
|
final uri = Uri(
|
|
scheme: 'https',
|
|
host: kServerHost,
|
|
port: kServerPort,
|
|
path: path,
|
|
);
|
|
|
|
final req = await client.openUrl(method, uri);
|
|
|
|
// Forward relevant headers
|
|
headers.forEach((name, values) {
|
|
if (_shouldForwardHeader(name)) {
|
|
for (final v in values) req.headers.add(name, v);
|
|
}
|
|
});
|
|
|
|
if (body.isNotEmpty) {
|
|
req.headers.contentLength = body.length;
|
|
req.add(body);
|
|
}
|
|
|
|
final res = await req.close();
|
|
final resBody = await res.fold<List<int>>([], (a, b) => a..addAll(b));
|
|
|
|
return KServerResponse(
|
|
statusCode: res.statusCode,
|
|
headers: res.headers,
|
|
body: Uint8List.fromList(resBody),
|
|
);
|
|
}
|
|
|
|
bool _shouldForwardHeader(String name) {
|
|
// 'authorization' is intentionally stripped: it carries the k_phone session
|
|
// token which is meaningless to k_server. k_server authenticates via the
|
|
// X-Proxy-Token header added by the Kotlin layer, not by bearer tokens.
|
|
const skip = {'host', 'connection', 'transfer-encoding', 'authorization'};
|
|
return !skip.contains(name.toLowerCase());
|
|
}
|
|
|
|
void close() => _client?.close();
|
|
}
|