Files
plezy/lib/watch_together/services/watch_together_relay_endpoint.dart
T

86 lines
2.6 KiB
Dart

/// Canonical HTTP(S) base endpoint for a Watch Together relay.
///
/// A base may include a reverse-proxy path prefix. The concrete health and
/// WebSocket routes are always appended as path segments.
final class WatchTogetherRelayEndpoint {
WatchTogetherRelayEndpoint._(this._baseUri);
static const String defaultBaseUrl = 'https://ice.plezy.app';
static final WatchTogetherRelayEndpoint defaultEndpoint = WatchTogetherRelayEndpoint._(Uri.parse(defaultBaseUrl));
final Uri _baseUri;
String get canonicalBaseUrl => _baseUri.toString();
Uri get healthUri => _appendPathSegment('health');
Uri get webSocketUri => _appendPathSegment('relay').replace(scheme: _baseUri.scheme == 'https' ? 'wss' : 'ws');
static WatchTogetherRelayEndpoint resolve(String? value) {
if (value == null || value.trim().isEmpty) return defaultEndpoint;
final endpoint = tryParseCustom(value);
if (endpoint == null) {
throw FormatException('Invalid Watch Together relay base URL');
}
return endpoint;
}
static WatchTogetherRelayEndpoint? tryParseCustom(String value) {
final trimmed = value.trim();
if (trimmed.isEmpty) return null;
final Uri uri;
try {
uri = Uri.parse(trimmed);
if (uri.hasPort && (uri.port < 1 || uri.port > 65535)) {
return null;
}
} on FormatException {
return null;
}
if ((uri.scheme != 'http' && uri.scheme != 'https') ||
!uri.isAbsolute ||
!uri.hasAuthority ||
uri.host.isEmpty ||
uri.userInfo.isNotEmpty ||
uri.hasQuery ||
uri.hasFragment) {
return null;
}
final normalized = uri.normalizePath();
var path = normalized.path;
while (path.endsWith('/')) {
path = path.substring(0, path.length - 1);
}
final hasDefaultPort =
normalized.hasPort &&
((normalized.scheme == 'http' && normalized.port == 80) ||
(normalized.scheme == 'https' && normalized.port == 443));
final canonical = Uri(
scheme: normalized.scheme,
host: normalized.host,
port: normalized.hasPort && !hasDefaultPort ? normalized.port : null,
path: path,
);
return WatchTogetherRelayEndpoint._(canonical);
}
Uri _appendPathSegment(String segment) {
final prefix = _baseUri.path;
return _baseUri.replace(path: '$prefix/$segment');
}
@override
bool operator ==(Object other) =>
identical(this, other) || other is WatchTogetherRelayEndpoint && other.canonicalBaseUrl == canonicalBaseUrl;
@override
int get hashCode => canonicalBaseUrl.hashCode;
@override
String toString() => canonicalBaseUrl;
}