Files
packages.dart/experimental/pocketbase_sync/test/simulation/toxiproxy_client.dart
T
2026-01-15 14:38:46 -08:00

177 lines
4.6 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
/// Controls the Toxiproxy daemon via its HTTP API.
class ToxiproxyController {
final String host;
final int port;
final Duration timeout;
ToxiproxyController({
this.host = 'localhost',
this.port = 8474,
this.timeout = const Duration(seconds: 5),
});
String get _apiBase => 'http://$host:$port';
/// Creates a new proxy.
Future<void> createProxy(String name, String listen, String upstream) async {
try {
final response = await http
.post(
Uri.parse('$_apiBase/proxies'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'name': name,
'listen': listen,
'upstream': upstream,
'enabled': true,
}),
)
.timeout(timeout);
if (response.statusCode != 201 && response.statusCode != 200) {
throw HttpException('Failed to create proxy: ${response.body}');
}
} on TimeoutException {
throw HttpException('Timeout creating proxy at $_apiBase');
}
}
/// Deletes a proxy.
Future<void> deleteProxy(String name) async {
try {
final response = await http
.delete(Uri.parse('$_apiBase/proxies/$name'))
.timeout(timeout);
if (response.statusCode != 204 && response.statusCode != 404) {
throw HttpException('Failed to delete proxy: ${response.body}');
}
} on TimeoutException {
throw HttpException('Timeout deleting proxy at $_apiBase');
}
}
/// Clears all proxies and toxics.
Future<void> reset() async {
try {
final response =
await http.post(Uri.parse('$_apiBase/reset')).timeout(timeout);
if (response.statusCode != 204) {
throw HttpException('Failed to reset toxiproxy: ${response.body}');
}
} on TimeoutException {
throw HttpException('Timeout resetting toxiproxy at $_apiBase');
}
}
/// Disables a proxy (Simulates connection cut).
Future<void> disable(String name) async {
await _updateState(name, false);
}
/// Enables a proxy (Simulates connection restore).
Future<void> enable(String name) async {
await _updateState(name, true);
}
Future<void> _updateState(String name, bool enabled) async {
try {
final response = await http
.post(
Uri.parse('$_apiBase/proxies/$name'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'enabled': enabled}),
)
.timeout(timeout);
if (response.statusCode != 200) {
throw HttpException('Failed to update proxy state: ${response.body}');
}
} on TimeoutException {
throw HttpException('Timeout updating proxy state at $_apiBase');
}
}
/// Adds a toxic (latency, jitter, etc) to a proxy.
Future<void> addToxic(String proxyName, Toxic toxic) async {
try {
final response = await http
.post(
Uri.parse('$_apiBase/proxies/$proxyName/toxics'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(toxic.toJson()),
)
.timeout(timeout);
if (response.statusCode != 200 &&
response.statusCode != 201 &&
response.statusCode != 409) {
throw HttpException('Failed to add toxic: ${response.body}');
}
} on TimeoutException {
throw HttpException('Timeout adding toxic at $_apiBase');
}
}
/// Removes all toxics from a proxy.
Future<void> deleteToxics(String proxyName) async {
// ...
}
}
class Toxic {
final String type;
final Map<String, dynamic> attributes;
final String? name; // Optional, Toxiproxy generates one if not provided.
final double toxicity;
Toxic({
required this.type,
this.attributes = const {},
this.name,
this.toxicity = 1.0,
});
Map<String, dynamic> toJson() {
final map = {
'type': type,
'attributes': attributes,
'toxicity': toxicity,
};
if (name != null) map['name'] = name!;
return map;
}
static Toxic latency({int latency = 1000, int jitter = 0}) {
return Toxic(
type: 'latency',
attributes: {
'latency': latency,
'jitter': jitter,
},
);
}
static Toxic bandwidth({required int rate}) {
return Toxic(
type: 'bandwidth',
attributes: {
'rate': rate, // KBs
},
);
}
static Toxic slowClose({required int delay}) {
return Toxic(
type: 'slow_close',
attributes: {
'delay': delay,
},
);
}
// Add more as needed: limit_data, slicer, timeout.
}